比赛链接
A - Super-palindrome (贪心)
题意:
给定一个字符串,每次可以改变任意一个字母,求使所有长度为奇数的子串变为回文串的最小操作次数
题目分析:
签到题,随便举个例子可以发现,满足要求的字符串必须满足奇数位和偶数位的字符全部相等
代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define pf push_front
#define int long long
#define fast ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
typedef set<int>::iterator SIT;
int dx[]={0,-1,0,1},dy[]={-1,0,1,0};
int gcd(int a,int b) {return b?gcd(b,a%b):a;}
int lcm(int a,int b) {return a/gcd(a,b)*b;}
const double eps=1e-6;
const int INF=0x3f3f3f3f,mod=1e9+7;
const int N=110;
int odd[N],even[N];
char s[N];
signed main(){
fast;
int T;
cin>>T;
while(T--){
memset(odd,0,sizeof(odd));
memset(even,0,sizeof(even));
cin>>s+1;
int n=strlen(s+1);
for(int i=1;i<=n;i++){
if(i&1) odd[s[i]-'a']++;
else even[s[i]-'a']++;
}
int maxv=-1;
int sum=0;
for(int i=0;i<26;i++)
maxv=max(maxv,odd[i]);
sum+=maxv;
maxv=-1;
for(int i=0;i<26;i++)
maxv=max(maxv,even[i]);
sum+=maxv;
cout<<n-sum<<endl;
}
return 0;
}
J - Master of GCD(差分+快速幂)
题意:
长度为n的数组初始化为1,然后进行m次操作,每次使区间 [l,r ]上的数乘上2或3,最后求区间的gcd
题目分析:
第一眼以为是线段树,然后抄了板子调了半天没调对(发现自己线段树写的好差),然后队友给的思路统计2和3出现的次数,答案就是2的最小次数次方乘上3的最小次数次方,数据较大,统计次数可以用差分数组来维护。
代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define pf push_front
#define int long long
#define fast ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
typedef set<int>::iterator SIT;
int dx[]={0,-1,0,1},dy[]={-1,0,1,0};
int gcd(int a,int b) {return b?gcd(b,a%b):a;}
int lcm(int a,int b) {return a/gcd(a,b)*b;}
const double eps=1e-6;
const int INF=0x3f3f3f3f,mod=998244353;
const int N=1e5+10;
int a2[N],b2[N];
int a3[N],b3[N];
int n,m;
int qmi(int a,int b,int p)
{
int res=1%p;
while(b){
if(b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
signed main()
{
fast;
int T;
cin>>T;
while(T--){
memset(a2,0,sizeof(a2));
memset(b2,0,sizeof(b2));
memset(a3,0,sizeof(a3));
memset(b3,0,sizeof(b3));
cin>>n>>m;
while(m--){
int l,r,c;
cin>>l>>r>>c;
if(c==2){
b2[l]++;
b2[r+1]--;
}
else if(c==3){
b3[l]++;
b3[r+1]--;
}
}
int min2=INF,min3=INF;
for(int i=1;i<=n;i++){
a2[i]=b2[i]+a2[i-1];
a3[i]=b3[i]+a3[i-1];
min2=min(min2,a2[i]);
min3=min(min3,a3[i]);
}
int ans=1;
ans=qmi(2,min2,mod);
ans=ans*qmi(3,min3,mod)%mod;
cout<<ans%mod<<endl;
}
return 0;
}
C - Hakase and Nano(思维+博弈)
题意:
给定n堆石子的个数和先后手顺序,第一个人可以选择一堆石子拿走至少一个石子,第二个人可以操作两次,谁拿走最后一堆谁赢,如果第二个人赢输出Yes,否则输出No
题目分析:博弈题必胜必败态的寻找是一个重要的解题点,通过分析我们可以得知第二个人如果先手,他必败的状态必定是n是3的倍数且所有堆的石子个数均为1,如果第一个人先手,我们可以思考什么情况下可以通过一次操作转化为第二个人的必败态,因为我们可以操作一次,一种情况是n-1为3的倍数且堆数为1的个数为n-1或n,另一种情况是n为3的倍数,且1的个数为n-1
代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define pf push_front
#define int long long
#define fast ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
typedef set<int>::iterator SIT;
int dx[]={0,-1,0,1},dy[]={-1,0,1,0};
int gcd(int a,int b) {return b?gcd(b,a%b):a;}
int lcm(int a,int b) {return a/gcd(a,b)*b;}
const double eps=1e-6;
const int INF=0x3f3f3f3f,mod=998244353;
const int N=1e6+10;
int n,op;
int a[N];
signed main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&op);
int cnt=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
if(a[i]==1) cnt++;
}
if(op==1){
if(n%3==0&&cnt==n)
puts("No");
else
puts("Yes");
}
else{
if((n%3==1&&cnt>=n-1)||(n%3==0&&cnt==n-1))
puts("No");
else
puts("Yes");
}
}
return 0;
}
B D 待补