D. Even String
题目链接:https://codeforces.com/contest/2086/problem/D
题意:
给定26个字母的出现次数,构造一个字符串
要求对于相同的一种字母,其任意个两两地在字符串的距离为偶数
思路:
要求同种字母的距离为偶数,即同种字母都在字符串奇数/偶数位置。((pos1-pos2)%2==0)
由于每个字母出现次数给定,则字符串长度已知
记odd为奇数位置的数目
那么我们只需要对26个字母进行01背包,挑选一些字母放在奇数位置,剩余的放在偶数位置。这是大体的方案数
记f[i]:为奇数位置已经填满了i个数时的方案数
f[odd]即为上述大体的方案数
那么枚举26个字母
以及j
f[j]=f[j]+f[j-a[i]]
第一项为不把这个字母放在奇数位置时的方案,第二项为把这个字母放在奇数位置时的方案
对于一个小方案,其方案数由多重集公式为:总数的阶乘 / 同种数的阶乘 的乘积
由于大数取模,需要用到阶乘的预处理以及乘法逆元
int fact[maxn];
int infact[maxn];
int ksm(int a,int b,int p){
int res=1;
while(b){
if(b&1)res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
void solve(){
vector<int>a(27);
int sum=0;
rep(i,1,26){
cin>>a[i];
sum+=a[i];
}
int odd,even;
if(sum%2==0)odd=even=sum/2;
else {
odd=sum/2;
even=sum/2+1;
}
vector<int>f(odd+1,0);
f[0]=1;
for(int i=1;i<=26;i++){
if(a[i]==0)continue;
for(int j=odd;j>=a[i];j--){
f[j]=(f[j]%mod+f[j-a[i]]%mod)%mod;
}
}
int cnt=f[odd];
int inv =1;
for(int i=1;i<=26;i++){
inv=inv*infact[a[i]]%mod;
}
int ans=((cnt*fact[odd])%mod * fact[even]%mod)*inv%mod;
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0);
int T;cin>>T;
fact[0]=infact[0]=1;
for(int i=1;i<maxn;i++){
fact[i]=fact[i-1]*i%mod;
infact[i]=infact[i-1]*ksm(i,mod-2,mod)%mod;
}
while(T--){
solve();
}
return 0;
}