A. Halloumi Boxes

题目链接

题意

有一个长度为n的数组,你每次操作能使一段区间反转,最长能反转的区间的长度是k,问进行若干次操作能否使数组变成非降序排列

分析

  • k>=2就可以一直选长度为2的区间反转,相当于交换相邻的两个数,这样就可以像冒泡排序那样排好序。
  • k=1说明不能进行任何操作,所以判断初始数组是否是非降序即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void solve(){
int n,k;
cin>>n>>k;
vector<int >vt(n+1);
for(int i=1;i<=n;i++){
cin>>vt[i];
}
if(k>=2){
cout<<"YES"<<endl;
return;
}
for(int i=1;i<=n-1;i++){
if(vt[i]>vt[i+1]){
cout<<"NO"<<endl;
return;
}
}
cout<<"YES"<<endl;
}

B. Rook

题目链接

分析

模拟题,根据题意模拟即可

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void solve(){
string s;
cin>>s;
char t=s[0];
char p=s[1];
for(int i=1;i<=8;i++){
string c="";
c+=t;
c+=(char)('0'+i);
if(c!=s){
cout<<c<<endl;
}
}
for(int i=0;i<8;i++){
string c="";
c+=(char)('a'+i);
c+=p;
if(c!=s){
cout<<c<<endl;
}
}
}

C. YetnotherrokenKeoard

题目链接

题意

有一个字符串,遇到$b$就删去最后出现的小写字母,遇到$B$则删去最后出现的大写字母

分析

模拟题。我们令$flag[n]$设为最后需要输出的元素(1代表需要输出,0代表不输出),把出现小写字母和大写字母的下标分别存入不同的数组,如果遇到$b$则在删去存小写字母下标的数组的最后一个元素并在$flag$里的对应元素操作,遇到$B$同理。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void solve(){
string s;
cin>>s;
vector<int>da;
vector<int>xia;
int n=s.length();
vector<int>flag(n);
for(int i=0;i<s.length();i++){
if(s[i]=='b'){
if(xia.size()!=0){
flag[xia.back()]=0;
xia.pop_back();
}
}
else if(s[i]=='B'){
if(da.size()!=0){
flag[da.back()]=0;
da.pop_back();
}
}
else{
flag[i]=1;
if(s[i]>='a' && s[i]<='z'){
xia.push_back(i);
}
else{
da.push_back(i);
}
}
}
for(int i=0;i<n;i++){
if(flag[i]){
cout<<s[i];
}
}
cout<<endl;
}

D. StORage room

题目链接

题意

有一个长度为$n$的数组$a$和一个$n*n$的表$M$,满足以下条件:
$$M_{i,j}=a_{i} | a_{j} (i!=j)$$
求一个满足条件的数组$a$,如果没有则输出$NO$
(输入满足$M_{i,j}=M_{j,i}$)

分析

我们把$a_{i}$的二进制数中1所在的位置记为集合$A$,把$a_{i}$和$a_{j}$取或运算得到的结果的二进制数中1的所在的位置记为集合$B_{j}$
可以发现,$A \subseteq B_{j} \quad (j=1, 2, \dots, n; \ j \neq i)$,所以我们把$B_{j}$取交集,这样得到的结果肯定满足条件。取交集反映在数字上就是
$$ a_{i} \&= M_{i,j} $$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void solve(){
int n;
cin>>n;
vector<vector<int> >vt(n+1,vector<int>(n+1));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>vt[i][j];
}
}
vector<int>ans(n+1);
for(int i=1;i<=n;i++){
int an=(1<<30)-1;
for(int j=1;j<=n;j++){
if(i==j){
continue;
}
an=(an&vt[i][j]);
}
ans[i]=an;
}
int flag=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if((ans[i]|ans[j])!=vt[i][j] && i!=j){
flag=0;
}
}
}
if(flag){
cout<<"YES"<<endl;
for(int i=1;i<=n;i++){
cout<<ans[i]<<" \n"[i==n];
}
}
else{
cout<<"NO"<<endl;
}
}

E. Theofanis’ Nightmare

题目链接

题意

把序列$a$分成若干子序列,设每个序列的位置为$i$,求所有子序列的和和子序列的位置的乘积的和的最大值

分析

我们假设一开始一个数是一个子序列,那么初始贡献就是$$ \sum_{i=1}^n a_{i}*i $$
然后我们从头开始遍历,假设当前这个数为$a_i$,如果这个数要和前面的数合并,对答案的贡献就是$-\sum_{j=i}^n a_{j}$,也就是说如果这个数的后缀和是负数,就可以使答案变大

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void solve(){
ll n;
cin>>n;
vector<int>vt(n+1);
ll ans=0;
for(int i=1;i<=n;i++){
cin>>vt[i];
ans+=(ll)(vt[i]*i);
}
vector<ll>sum(n+2);
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+vt[i];
}
for(int i=2;i<=n;i++){
if(sum[n]-sum[i-1]<0){
ans-=(sum[n]-sum[i-1]);
}
}
cout<<ans<<endl;
}

F. Maximum And Queries (easy version)

题目链接

题意

有一数组$a$和$q$次询问,每次询问都会给你一个$k$,你可以对数组进行最多$k$次操作,每次操作你都可以对数组中的数+1,问你数组的与和最大是多少

分析

对于easy version每次询问可以暴力一点。如果答案的二进制数的某一位是1,那么数组中的所有数这一位一定是1,所以我们贪心考虑。从最高位开始,计算要让所有数的这一位为1需要多少次操作,如果足够,答案则加上它,如果不够再考虑低一位

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void solve() {
int n, q;
cin >> n >> q;
vector<ll> vt(n+1);
for (int i = 1; i <= n; i++){
cin >> vt[i];
}
vector<ll>v=vt;
while (q--) {
ll k;
cin >> k;
ll ans = 0;
vt=v;
for (int b = 60; b >= 0; b--) {
ll p=(1ll<<b);
ll sum=0;
for(int i=1;i<=n;i++){
if((vt[i]&p)==0){
sum=(ll)sum+p-(vt[i]%p);
}
if(sum>k){
break;
}
}
if(sum<=k){
k-=sum;
ans+=p;
for(int i=1;i<=n;i++){
if((vt[i]&p)==0){
vt[i]-=vt[i]%p;
vt[i]+=p;

}
}
}
}
cout << ans << endl;
}
}