cf1620g Subsequences Galore

给定一个字符串序列 [t1,t2,,tm] , 定义 f([t1,t2,,tm]) 为至少是其中一个字符串 ti 的子序列的字符串个数,其中 f([])=0 .

给定哟个字符串序列 [s1,s2,,sm] 对每一个子集 [si1,si2,,sik] 求出 f([si1,si2,,sik])998,244,353 取模后的值。

输出 f([si1,si2,,sik])×k×(i1+i+2++ik) 的异或和(不取模)。

注意每个字符串 si 中的字母都是排好序的。

1n23,1|si|2104

正着求 f(S) 很难,考虑反着,那就是 容斥

g(S) 表示字符串集合为 S 时,在 S 中每一个集合都出现的字符串个数 . 此时的时间复杂度为 O(||n2n),这个大概在 5109 左右,时限是 10s ,可以跑过去 .

对于每个 f(S) 可以通过枚举子集以 O(3n) 的时间复杂度求出,此时这个做法的时间复杂度就是 O(n2n+3n) . 很显然,过不去 .

code
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline") //Optimization flags
#pragma GCC option("arch=native","tune=native","no-zero-upper") //Enable AVX
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#include<bits/stdc++.h>
using namespace std;
char in[1000005];
int iiter=0,llen=0;
inline char get(){
	if(iiter==llen)llen=fread(in,1,100000,stdin),iiter=0;
	if(llen==0)return EOF;
	return in[iiter++];
}
inline int rd(){
	char ch=get();while(ch<'0'||ch>'9')ch=get();
	int res=0;while(ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+ch-'0',ch=get();
	return res;
}
inline void pr(long long res){
	if(res==0){putchar('0');return;}
	static int out[20];int len=0;
	while(res)out[len++]=res%10,res/=10;
	for(int i=len-1;i>=0;i--)putchar(out[i]+'0');
}
inline string getstr(){
	char ch=get();string res="";
	while(isspace(ch))ch=get();
	while(!isspace(ch))res+=ch,ch=get();
	return res;
}
const int mod=998244353;
int n;
string s[24];
int builtin_popcount[1<<24];
int f[1<<24],g[1<<24];
int cnt[24][30];
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	n=rd();
	for(int i=0;i<n;i++)s[i]=getstr();
	for(int msk=0;msk<(1<<n);msk++){
		int tmp=0;
		for(int i=0;i<n;i++)if(msk&(1<<i))tmp++;
		builtin_popcount[msk]=tmp;
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<(int)s[i].size();j++)
			cnt[i][s[i][j]-'a']++;
	}
	for(int msk=0;msk<(1<<n);msk++)f[msk]=1;
	for(int msk=1;msk<(1<<n);msk++){
		int mx[26];memset(mx,0x3f3f3f3f,sizeof(mx));
		for(int i=0;i<n;i++)if(msk&(1<<i))
			for(int j=0;j<26;j++)mx[j]=min(mx[j],cnt[i][j]);
		for(int i=0;i<26;i++)f[msk]=1ll*f[msk]*(mx[i]+1)%mod;
	}
	for(int msk=0;msk<(1<<n);msk++)f[msk]=(f[msk]-1+mod)%mod;
	memset(g,0,sizeof(g));
	for(int msk=0;msk<(1<<n);msk++){
		for(int sub=msk;;sub=(sub-1)&msk){
			if(builtin_popcount[sub]&1)g[msk]=(g[msk]+f[sub])%mod;
			else g[msk]=(g[msk]-f[sub]+mod)%mod; 
			if(sub==0)break;
		}
	}
	long long ans=0;
	for(int msk=0;msk<(1<<n);msk++){
		long long tmp=0;
		for(int i=0;i<n;i++)if(msk&(1<<i))tmp+=i+1;
		tmp=1ll*tmp*builtin_popcount[msk]*(g[msk]+1);
		ans=ans^tmp; 
	}
	pr(ans);
	return 0;
}
/*inline? ll or int? size? min max?*/

但是此时,通过仔细思考,可以发现,在复杂度瓶颈 O(3n) 的地方 .

for(int msk=0;msk<(1<<n);msk++){
	for(int sub=msk;;sub=(sub-1)&msk){
		if(builtin_popcount[sub]&1)g[msk]=(g[msk]+f[sub])%mod;
		else g[msk]=(g[msk]-f[sub]+mod)%mod; 
		if(sub==0)break;
	}
}

|S| 是偶数的时候,将 g(S) 取反,那么 f(S) 就等于子集相加,可以用高维前缀和做到 . 于是将 O(3n) 优化到 O(n2n) .

时间复杂度 : O(||n2n+n2n)

空间复杂度 : O(2n)

code
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline") //Optimization flags
#pragma GCC option("arch=native","tune=native","no-zero-upper") //Enable AVX
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2")
#include<bits/stdc++.h>
using namespace std;
char in[1000005];
int iiter=0,llen=0;
inline char get(){
	if(iiter==llen)llen=fread(in,1,100000,stdin),iiter=0;
	if(llen==0)return EOF;
	return in[iiter++];
}
inline int rd(){
	char ch=get();while(ch<'0'||ch>'9')ch=get();
	int res=0;while(ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+ch-'0',ch=get();
	return res;
}
inline void pr(long long res){
	if(res==0){putchar('0');return;}
	static int out[20];int len=0;
	while(res)out[len++]=res%10,res/=10;
	for(int i=len-1;i>=0;i--)putchar(out[i]+'0');
}
inline string getstr(){
	char ch=get();string res="";
	while(isspace(ch))ch=get();
	while(!isspace(ch))res+=ch,ch=get();
	return res;
}
const int mod=998244353;
int n;
string s[24];
int builtin_popcount[1<<24];
int f[1<<24];
int cnt[24][30];
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	n=rd();
	for(int i=0;i<n;i++)s[i]=getstr();
	for(int msk=0;msk<(1<<n);msk++){
		int tmp=0;
		for(int i=0;i<n;i++)if(msk&(1<<i))tmp++;
		builtin_popcount[msk]=tmp;
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<(int)s[i].size();j++)
			cnt[i][s[i][j]-'a']++;
	}
	for(int msk=0;msk<(1<<n);msk++)f[msk]=1;
	for(int msk=1;msk<(1<<n);msk++){
		int mx[26];memset(mx,0x3f3f3f3f,sizeof(mx));
		for(int i=0;i<n;i++)if(msk&(1<<i))
			for(int j=0;j<26;j++)mx[j]=min(mx[j],cnt[i][j]);
		for(int i=0;i<26;i++)f[msk]=1ll*f[msk]*(mx[i]+1)%mod;
	}
	for(int msk=0;msk<(1<<n);msk++){
		f[msk]=(f[msk]-1+mod)%mod;
		if(builtin_popcount[msk]&1)continue;
		f[msk]=(mod-f[msk])%mod;
	}
	for(int i=0;i<n;i++){
		for(int msk=0;msk<(1<<n);msk++)if(msk&(1<<i))
			f[msk]=(f[msk^(1<<i)]+f[msk])%mod;
	}
	long long ans=0;
	for(int msk=0;msk<(1<<n);msk++){
		long long tmp=0;
		for(int i=0;i<n;i++)if(msk&(1<<i))tmp+=i+1;
		tmp=1ll*tmp*builtin_popcount[msk]*(f[msk]+1);
		ans=ans^tmp; 
	}
	pr(ans);
	return 0;
}
/*inline? ll or int? size? min max?*/
posted @   xyangh  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示