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;
}
posted @ 2022-03-18 09:01  violet_holmes  阅读(69)  评论(1编辑  收藏  举报