补题*总结题21/9/11
A - Super-palindrome(思维)
题意:
给一个字符串,问修改最少多少次,把给定字符串修改成超级回文串。
“超级回文串”的定义:对于所有长度为奇数的子串,都是回文的字符串就是超级回文串,
思路:
枚举发现:
超级回文串就是奇数位是同一个字符,偶数位是同一字符。
所以:
直接统计奇数位上最多的字符数量sum1,偶数位上最多的字符数量sum2,答案一定是n-sum1-sum2,(n为给定串长度)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
int t;
cin>>t;
while (t--)
{
char a[101];
cin>>a;
int len=strlen(a);
int b[27]={0};
int c[27]={0};
for(int i=0;i<len;i++)
{
if(i%2==0) b[ a[i]-'a' ]++;
else
c[ a[i]-'a' ]++;
}
int bmax=0;
int cmax=0;
int bsum=0;
int csum=0;
for(int i=0;i<27;i++)
{
bsum+=b[i];
csum+=c[i];
if(bmax<b[i]) bmax=b[i];
if(cmax<c[i]) cmax=c[i];
}
cout<<csum-cmax+bsum-bmax<<endl;
}
return 0;
}
C - Hakase and Nano(博弈+规律)
题意:
有T组数据,
每组数据输入一个n(石子堆的数目)d(1表示Ha先取,2表示Na先取)
然后输入n个数据表示第i堆石子有ai个石子。
要求:
- 每一次取:可以去任意一堆石子取石子,但最少取一个,
- Hakase每次必须取两次,Nano每次必须取一次。
- Hakase赢就输出Yes否则输出No。
思路:
博弈的关键是找到必输(赢)情况,然后看其他情况怎么能转化为必输情况
通过枚举发现,
Ha先手:Ha必输情况是n是3的倍数+a[i]都是1,其余情况都是赢
故反推
Na先手:Ha必输的情况是Na通过一步操作可以达到,Ha必输的情况
即:
- H在n是3的倍数,且有n-1个数为1时会输(这时N只需从不是1的那堆石子里拿掉一些石子使状态变为1 1 1)
- n是3的倍数余1,且n个数为1时会输(此时N只需拿掉一堆石子,H就到了必输态),
- n是3的倍数余1,且n-1个数为1时会输(此时N只需拿掉一堆不是1的石子,H就到了必输态)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
int n,d;
scanf("%d%d",&n,&d);
int a[n+2];
for(int i=0; i<n; i++)
{
cin>>a[i];
}
if(n==1)
{
if(d==1)
{
cout<<"Yes"<<endl;
continue;
}
else
{
cout<<"No"<<endl;
continue;
}
}
if(n==2)
{
cout<<"Yes"<<endl;
continue;
}
sort(a,a+n);
if(n==3)
{
if(a[0]==1&&a[1]==1&&a[2]==1)
{
if(d==1)
{
cout<<"No"<<endl;
continue;
}
else
{
cout<<"Yes"<<endl;
continue;
}
}
if(a[0]==1&&a[1]==1&&a[2]>1)
{
if(d==1)
{
cout<<"Yes"<<endl;
continue;
}
else
{
cout<<"No"<<endl;
continue;
}
}
if(a[0]==1&&a[1]>1&&a[2]>1)
{
if(d==1)
{
cout<<"Yes"<<endl;
continue;
}
else
{
cout<<"Yes"<<endl;
continue;
}
}
}
if(n>=4)
{
int o=a[n-2];
if(d==1)
{
if(n%3==0&&a[n-1]==1)
{
cout<<"No"<<endl;
continue;
}
else
{
cout<<"Yes"<<endl;
continue;
}
}
else
{
if((n-1)%3==0&&o==1)
{
cout<<"No"<<endl;
continue;
}
else if(n%3==0&&o==1&&a[n-1]!=1)
{
cout<<"No"<<endl;
continue;
}
else
{
cout<<"Yes"<<endl;
continue;
}
}
}
}
return 0;
}
J - Master of GCD(差分/线段树+快速幂)
题意:
给你一个全是1的数组,每次选一个区间,对区间中所有数进行乘x的操作(x== 2||x ==3),问你最后所有数的gcd是多少(对998244353取模)。
思路:
- 线段树维护区间加法,
-
- 最后看大区间里最少的2和3的个数,这就是所有数共有的因子,把它算出来就是gcd了,
- 差分维护两个数组
-
- 一个数组记录2的个数,一个记录3的个数,差分数组还原之后就找两个最小值。
代码:
- 一个数组记录2的个数,一个记录3的个数,差分数组还原之后就找两个最小值。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
ll qpow(ll a,ll b)
{
ll ans=1;
a%=mod;
while(b)
{
if(b&1)
ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
int n,m;
scanf("%d%d",&n,&m);
ll a[n+2]={0};
ll b[n+2]={0};
ll i;
int l,r,x;
for(i=0;i<m;i++)
{
scanf("%d%d%d",&l,&r,&x);
if(x==2)
{
a[l]++; //差分操作
a[r+1]--; //差分操作
}
else
{
b[l]++; //差分操作
b[r+1]--; //差分操作
}
}
ll mia=1e18;
ll mib=1e18;
for(i=1;i<=n;i++)
{
a[i]+=a[i-1];
b[i]+=b[i-1];
mia=min(mia,a[i]);//遍历取最小值
mib=min(mib,b[i]);//遍历取最小值
}
printf("%lld\n",qpow(2,mia)*qpow(3,mib)%mod);
}
return 0;
}