Codeforces Round #770(Div. 2)
A-Reverse and Concatenate
规定\(rev(s)\)操作能把字符串\(s\)反转,现有两种操作:\(1、\)把\(s\)变成\(s+rev(s)\);\(2、\)把\(s\)变成\(rev(s)+s\)。有\(t\)次询问,每次你有一个长度为\(n\)的字符串\(s\),你能进行\(k\)次操作,问你能把原串变成多少种不同的串,输出答案。\((1\leq t\leq100,1\leq n\leq100,0\leq k\leq1000)\)
思路
我们很容易能知道,进行一次操作就能得到一个回文串,而回文串进行的两种操作得到的答案相同,所以如果一开始就是回文串,答案就是\(1\),如果不是,如果\(k\)为\(0\),则答案为\(1\),否则,答案为\(2\),就结束了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[1000005];
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
int n,t;
scanf("%d%d",&n,&t);
scanf("%s",s+1);
bool ck=1;
for(int i=1;i<=n>>1;i++)if(s[i]!=s[n-i+1]){ck=0;break;}
if(ck)printf("1\n");
else
{
if(t==0)printf("1\n");
else printf("2\n");
}
}
return 0;
}
B-Fortune Telling
有\(t\)次询问,每次有一个\(n,x,y\),然后是\(n\)个非负数\(a_1,a_2,…,a_n\),对于每个数有两种操作:\(1、\)把\(x\)变成\(x+a_i\);\(2、\)把\(x\)变成\(x\oplus a_i\)。问经过\(n\)次操作后,\(x\)还是\(x+3\)能变成\(y\)。\((1\leq t\leq10^4,1\leq n\leq10^5,0\leq x\leq10^9,0\leq y\leq10^{15},0\leq a_i\leq10^9)\)
思路
蒟蒻不会,看了题解也只能勉强做出。首先我们要知道的是\(+\)运算和\(\oplus\)对数值奇偶性的改变是一样的,而且\(x\)和\(x+3\)的奇偶性不同,而且\(x\)和\(x+3\)中必有一个能变成\(y\),因此问题就轻轻松松被解决了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
int n,x;
ll y;
scanf("%d%d%lld",&n,&x,&y);
int num=0;
int a;
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
if(a%2)num++;
}
if(x%2==y%2)
{
if(num%2)printf("Bob\n");
else printf("Alice\n");
}
else
{
if(num%2)printf("Alice\n");
else printf("Bob\n");
}
}
return 0;
}
C-OKEA
有\(t\)次询问,每次询问有一个\(n,k\),输出的格式为“\(YES\)”加上\(n\)行\(k\)列\(1\)到\(n\cdot k\)中的值(且每一项仅能输出一次)或“\(NO\)”。如果能够满足每一行中任意\(l\)到\(r\)的平均值为一个整数,则输出前者,否则,输出后者。\((1\leq t\leq500,1\leq n,k\leq100)\)
思路
显然我们可以知道\(k\)为\(1\)时可以直接按顺序输出,同时我们也可以知道在其余情况中,\(n\)为奇数的情况一定为“\(NO\)”,偶数的情况一定为“\(YES\)”,且经过简单构造后就能直接\(AC\)啦。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int _;
scanf("%d",&_);
while(_--)
{
int n,k;
scanf("%d%d",&n,&k);
if(k==1)
{
printf("YES\n");
for(int i=1;i<=n;i++)printf("%d\n",i);
}
else if(k>=2)
{
if(n%2)printf("NO\n");
else
{
printf("YES\n");
for(int i=1;i<=n>>1;i++)
{
for(int j=k;j>=1;j--)
{
printf("%d ",i*2*k-2*j+1);
}
printf("\n");
for(int j=k;j>=1;j--)
{
printf("%d ",i*2*k-2*j+2);
}
printf("\n");
}
}
}
}
return 0
}
E-Fair Share
有\(m\)组数据,每组有\(n(n为偶数)\)个元素。现要求你将每组数据分成个数相等的两份放到可重集合\(L\)和\(R\)中,问能否使得最后\(L\)和\(R\)相等,若可以,输出“\(YES\)”,并输出每个元素的去向,否则,输出“\(NO\)”。\((1\leq m\leq10^5,2\leq n\leq2\cdot10^5,1\leq a_i\leq10^9)\)
思路
考虑二分图找欧拉回路,左部是\(m\)个点,右部是\(cnt(元素个数)\)个点,然后向左指代表到\(L\)集合,向右指代表到\(R\)集合中,然后就一直搜环就可以了。这里也附上\(Solemntee\)大佬的题解。
代码
#include<bits/stdc++.h>
using namespace std;
vector<vector<int>>a(1e5+5),ans(1e5+5);
map<int,int>mp,num;
vector<vector<pair<int,int>>>v(4e5+5);
vector<int>cnt(4e5+5);
void dfs(int x,int dir)
{
int n=x;
cnt[n]--;
while(cnt[n]>-1&&ans[min(n,v[n][cnt[n]].first)][v[n][cnt[n]].second]!=-1)cnt[n]--;
if(cnt[n]==-1)return;
ans[min(n,v[n][cnt[n]].first)][v[n][cnt[n]].second]=dir;
dfs(v[n][cnt[n]].first,1-dir);
}
int main()
{
int m;
scanf("%d",&m);
int tot=0;
for(int i=1;i<=m;i++)
{
int n;
scanf("%d",&n);
a[i].resize(n+1);
ans[i].resize(n+1,-1);
for(int j=1;j<=n;j++)
{
scanf("%d",&a[i][j]);
if(!mp[a[i][j]])mp[a[i][j]]=++tot;
num[mp[a[i][j]]]++;
}
}
for(int i=1;i<=tot;i++)if(num[i]%2)
{
printf("NO\n");
return 0;
}
int MAX=2e5;
for(int i=1;i<=m;i++)
{
for(int j=1;j<a[i].size();j++)
{
v[i].push_back({mp[a[i][j]]+MAX,j});
v[mp[a[i][j]]+MAX].push_back({i,j});
cnt[i]++;
cnt[mp[a[i][j]]+MAX]++;
}
}
for(int i=1;i<=m;i++)dfs(i,0);
printf("YES\n");
for(int i=1;i<=m;i++)
{
for(int j=1;j<ans[i].size();j++)
{
if(ans[i][j])printf("L");
else printf("R");
}
printf("\n");
}
return 0;
}
F-Fibonacci Additions
规定一个斐波那契加法:从\(l\)到\(r\),对\(a_l,a_l+1,...,a_r\)分别加上\(F_1,F_2,...,F_{r-l+1}\),并且对\(MOD\)进行取模,其中\(F_1=1,F_2=1,F_i=F_{i-1}+F_{i-2}\)。现在有两个同样长度的数组\(A\)和\(B\),有\(q\)次操作,每次选择其中一个数组,对于\(l\)到\(r\)进行斐波那契加法。每次操作结束后,询问两数组是否相等。\((1\leq n,q\leq3\cdot10^5,1\leq MOD\leq10^9+7)\)
思路
相比\(B\)题,这题就出的非常巧妙。首先要使\(A\)和\(B\)相等,那么就可以构造一个\(C\),使得\(C_i=A_i-B_i\)。而要加的又是斐波那契数列,那么可以想到差分。再构造一个数组\(D\),使得\(D_1=C_1,D_2=C_2-C_1,D_i=C_i-C_{i-1}-C_{i-2}\),那么就是在问\(D\)数组中有没有不是\(0\)的数字,而对\(A\)和\(B\)进行斐波那契加法就能转换为\(D_l+1,D_{r+1}-F_{r-l+2},D_{r+2}-F_{r-l+1}\)。这样问题就能被轻松解决了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[300005],b[300005];
ll c[300005],d[300005];
ll F[300005];
int main()
{
int n,q;
ll mod;
scanf("%d%d%lld",&n,&q,&mod);
F[1]=1;F[2]=1;
for(int i=3;i<=n;i++)F[i]=(F[i-1]+F[i-2])%mod;
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
for(int i=1;i<=n;i++)c[i]=((a[i]-b[i])%mod+mod)%mod;
d[1]=c[1];d[2]=((c[2]-c[1])%mod+mod)%mod;
for(int i=3;i<=n;i++)
d[i]=((c[i]-c[i-1]-c[i-2])%mod+mod)%mod;
int num=0;
for(int i=1;i<=n;i++)num+=(d[i]!=0);
while(q--)
{
char s;
int l,r;
scanf("\n%c%d%d",&s,&l,&r);
if(s=='A')
{
num-=(d[l]!=0);
(d[l]+=1)%=mod;
num+=(d[l]!=0);
if(r+1<=n)
{
num-=(d[r+1]!=0);
((d[r+1]-=F[r-l+2])%=mod+mod)%=mod;
num+=(d[r+1]!=0);
}
if(r+2<=n)
{
num-=(d[r+2]!=0);
((d[r+2]-=F[r-l+1])%=mod+mod)%=mod;
num+=(d[r+2]!=0);
}
}
else
{
num-=(d[l]!=0);
((d[l]-=1)%=mod+mod)%=mod;
num+=(d[l]!=0);
if(r+1<=n)
{
num-=(d[r+1]!=0);
(d[r+1]+=F[r-l+2])%=mod;
num+=(d[r+1]!=0);
}
if(r+2<=n)
{
num-=(d[r+2]!=0);
(d[r+2]+=F[r-l+1])%=mod;
num+=(d[r+2]!=0);
}
}
if(num)printf("NO\n");
else printf("YES\n");
}
return 0;
}
结论
对于将一个数组的一个区间同时加上另一个数组且加上的这个数组中的元素都能通过同样的规律从前面的元素中算得的这样一类题目都能通过差分来思考。