Atcoder Grand Contest 039C(容斥原理,计数DP)

//每次操作相当于将最低位取反加到最高位(N~1位)
#define HAVE_STRUCT_TIMESPEC
#include<bits/stdc++.h>
using namespace std;
char s[200007];
int mi2[200007],num[200007];
const int mod = 998244353;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int n;
cin>>n>>s+1;
mi2[0]=1;
for(int i=1;i<=n;++i)
mi2[i]=2ll*mi2[i-1]%mod;
for(int i=1;i<=n;i+=2)if(n%i==0){//i取偶数会导致经过frequency次操作得到的根本不是它的取反,(奇数段才能经过frequency次操作后首尾相连),任何数经过n次操作都会变成它的取反,相当于每一位都从后向前倒了一遍
int frequency=n/i;//从大到小枚举次数的一半,即经过frequency次操作使得串变成它的取反,长度长的数可以短的数必定可以,所以在计数时frequency中包含了一些长度短但是经过frequency的因数次操作就满足提议的答案,通过容斥原理在计算长度短的答案时将它们在长度长的答案里减去
bool flag=1;
for(int j=1;j<=n&&flag;++j){
if(s[j]=='1'){//这一位为1,那么必定有长度为n的串,分为frequency段,每一段是相邻段的取反,且前frequency个字符不比s大
if(j<=frequency||s[j-frequency]=='1'){//特判以frequency个0开头的串是否比s小
num[frequency]=(num[frequency]+(j<=frequency?mi2[frequency-j]:1))%mod;
}
}
if(j>frequency&&s[j]==s[j-frequency])//特判以frequency个0开头的串是否比s小
flag=0;
}
if(flag)//如果以frequency个0开头的串不比s小,就可以+1,特判只要找不要有相隔frequency个位置的两个字符都是'0'
num[frequency]=(num[frequency]+1)%mod;
cout<<num[frequency]<<"\n";
}
int ans=0;
for(int i=1;i<=n;++i)if(n%i==0&&(n/i)&1&&num[i]){//从小到大枚举长度
ans=(ans+2ll*num[i]*i)%mod;//经过i次操作变为原串的取反需要再经过i次操作才变回原串
for(int j=i*2;j<=n;j+=i)//容斥原理,把已经计算在内的长度较短的数字在长度较长的答案里减去
num[j]=(num[j]+mod-num[i])%mod;
}
cout<<ans;
return 0;
}

猜你喜欢

转载自www.cnblogs.com/ldudxy/p/11666957.html