CF1620G Solution
题解
可以发现,字符串子序列并集的大小即为 \(f\) 函数,并集可以由交集容斥得出,因此考虑交集。对于字符串 \(s_i\) ,\(f(s_i)=\sum\limits_{x='a'}^{'z'}(cnt_{i,x}+1)\),其中 \(cnt_{i,x}\) 表示字符 \(x\) 在 \(s_i\) 中出现的次数。设 \(g(s)\) 表示字符串集 \(s\) 中字符串子序列交集的大小,\(g(s)=\sum\limits_{x='a'}^{'z'}(mi_x+1),\ mi_x=min_{s_i \in s}(cnt_{i,x})\)。由此可得 \(f(s)=\sum\limits_{t\in s}(-1)^{size(t)} g(t)\) ,奇偶分开后用SOS DP(高维前缀和)求出即可。
SOS DP
用于求解 \(f_i=\sum\limits_{j\in i} a_j\) 类问题,\(f_i=a_i+\sum\limits f_{i\oplus 2^j}\ (i \& 2^j=2^j)\)。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=27,M=2e4+10,srr=8388610,mod=998244353;
char s[N][M]; ll f[srr],g[srr];
int num[N][N],mi[srr][N];
inline int read()
{
int ss=0,ww=1; char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-') ww=-1; ch=getchar();}
while(ch>='0' && ch<='9') ss=(ss<<1)+(ss<<3)+(ch^48),ch=getchar();
return ss*ww;
}
int main()
{
int n=read(),tmp; ll ans=0,res;
for(int i=0;i<n;i++) scanf("%s",s[i]);
for(int i=0;i<n;i++)
{
tmp=strlen(s[i]);
for(int j=0;j<tmp;j++) num[i][s[i][j]-'a']++;
}
memset(mi,0x3f,sizeof(mi));
//求出mi和g(此处数组名f)
for(int i=1;i<(1<<n);i++)
{
f[i]=1,tmp=log2(i&(-i));
for(int j=0;j<26;j++)
mi[i][j]=min(mi[i^(i&(-i))][j],num[tmp][j]),f[i]=f[i]*(mi[i][j]+1)%mod;
}
//奇偶分开
for(int i=1;i<(1<<n);i++)
if(__builtin_popcount(i)&1) g[i]=f[i],f[i]=0;
//SOS DP
for(int i=0;i<n;i++)
for(int j=1;j<(1<<n);j++)
if(j&(1<<i)) f[j]=(f[j]+f[j^(1<<i)])%mod,g[j]=(g[j]+g[j^(1<<i)])%mod;
for(int i=0;i<(1<<n);i++)
{
res=(g[i]-f[i]+mod)%mod*__builtin_popcount(i),tmp=0;
for(int j=0;j<n;j++)
if(i&(1<<j)) tmp+=j+1;
res*=tmp,ans^=res;
}
printf("%lld",ans);
return 0;
}