[NOI2025] 集合 题解

去不了 NOI 的菜鸡终于把集合看懂了,写个博客加深一下印象。
[NOI2025] 集合

要求:

\[ans=\sum_P \sum_Q [f(p)=f(Q)][P\cap Q = \emptyset] \prod_{i\in P\cup Q} a_i \]

先处理这题比较特殊的 \([f(p)=f(Q)]\),考虑枚举 \(f(P)=f(Q)=S\)\(f(P),f(Q)\) 恰好等于 \(S\) 不好做,容斥一下变成包含:

\[\begin{aligned} ans &= \sum_S \sum_{S\subseteq T_P} \sum_{S\subseteq T_Q} \sum_{T_P \subseteq f(P)} \sum_{T_Q \subseteq f(Q)} (-1)^{|T_P|-|S|} (-1)^{|T_Q|-|S|} [P\cap Q = \emptyset] \prod_{i\in P\cup Q} a_i\\ &= \sum_S \sum_{S\subseteq T_P} \sum_{S\subseteq T_Q} \sum_{T_P \subseteq f(P)} \sum_{T_Q \subseteq f(Q)} (-1)^{|T_P|+|T_Q|-2|S|} [P\cap Q = \emptyset] \prod_{i\in P\cup Q} a_i\\ \end{aligned} \]

发现容斥系数上的 \(-2|S|\) 没有用,可以去掉,所以也就没有必要枚举 \(S\),只需要枚举 \(T_P,T_Q\) 然后乘上合法的 \(S\) 数量即可,而合法的 \(S\) 满足 \(S\subseteq T_P,T_Q\) 也即 \(S\subseteq T_P\cap T_Q\),所以合法的 \(S\) 数量为 \(2^{|T_P\cap T_Q|}\)。为了美观,下面用 \(S,T\) 分别代替 \(T_P,T_Q\)

\[\begin{aligned} ans &= \sum_{S} \sum_{T} (-1)^{|S|+|T|} 2^{|S\cap T|} \sum_{S \subseteq f(P)} \sum_{T \subseteq f(Q)} [P\cap Q = \emptyset] \prod_{i\in P\cup Q} a_i\\ \end{aligned} \]

\(S \subseteq f(P)\) 相当于什么,其实就是说 \(P\) 中的数都要是 \(S\) 的超集,我们设 \(R_S\)\(S\) 的超集构成的集合,则 \(P\subseteq R_S\),于是我们就把跟 \(f\) 有关的东西去掉了。
然后处理 \([P\cap Q = \emptyset]\),我们直接去枚举 \(R=P\cup Q\),显然 \(R\subseteq R_S\cup R_T\)。那么对于 \(i\in R\),若 \(i\) 只属于 \(R_S\)\(R_T\) 中的一个,则 \(i\) 只能放到那一个,否则 \(i\) 放到哪一个都可以。

\[\begin{aligned} ans &= \sum_{S} \sum_{T} (-1)^{|S|+|T|} 2^{|S\cap T|} \sum_{R\subseteq R_S\cup R_T} \prod_{i\in R} (1+[i\in R_S\cap R_T])a_i\\ &= \sum_{S} \sum_{T} (-1)^{|S|+|T|} 2^{|S\cap T|} (\prod_{i\in R_S\cup R_T \setminus R_S\cap R_T} (a_i+1)) (\prod_{i\in R_S\cap R_T} (2a_i+1))\\ \end{aligned} \]

\(f_S=\prod_{i\in R_S} (a_i+1),g_S=\prod_{i\in R_S} (2a_i+1)\),这两个可以高维后缀积 \(O(n2^n)\) 预处理。又发现 \(R_S\cap R_T=R_{S\cup T}\)

\[ans = \sum_{S} \sum_{T} (-1)^{|S|+|T|} 2^{|S\cap T|} f(S)f(T)\dfrac{g(S\cup T)}{f(S\cup T)^2} \]

然后这里有个问题是分母可能为 \(0\),有一个 trick 是直接把每个数用 \(x\times 0^k\) 的形式存储,表示这个数里面目前有 \(k\)\(0\),然后就可以做除法了。
发现这个式子里同时有 \(S\cap T\)\(S\cup T\),但是 \(S\cap T\) 我们只关心他的大小,所以我们可以用 \(|S\cap T|=|S|+|T|-|S\cup T|\) 来换掉:

\[\begin{aligned} ans &= \sum_{S} \sum_{T} (-1)^{|S|+|T|} 2^{|S|+|T|-|S\cup T|} f(S)f(T)\dfrac{g(S\cup T)}{f(S\cup T)^2} \\ &= \sum_{S} \sum_{T} (-2)^{|S|}f(S) \times (-2)^{|T|}f(T) \times \dfrac{g(S\cup T)}{2^{|S\cup T|}f(S\cup T)^2} \end{aligned} \]

\(F(S)=(-2)^{|S|}f(S),h=F\times F\),这里 \(\times\) 是或卷积,则:

\[ans=\sum_S \dfrac{h(S)g(S)}{2^{|S|}f(S)^2} \]

最后一个问题是,这里 \(F\) 是用 \(x\times 0^k\) 的形式存储,但是做 FMT 的时候涉及到加减法,当 \(k_1\ne k_2\) 时,两个数 \(x1\times 0^{k_1},x2\times 0^{k_2}\) 无法简单的加减法。
不过注意到我们在除以 \(f(S)^2\) 之后不可能除出来一个 \(0\) 的负次幂,所以对于分子中 \(0\) 的非最低次项他除完分母之后 \(0\) 的幂一定 \(>0\),所以贡献一定为 \(0\),因此在做加减法的时候直接舍弃次幂大的那个即可。

code

#include<bits/stdc++.h>
#define Debug puts("-------------------------")
using namespace std;
const int N=(1<<20)+5,mod=998244353,inv2=(mod+1)/2;

inline int read(){
	int w=1,s=0;
	char c=getchar();
	for(;c<'0'||c>'9';w*=(c=='-')?-1:1,c=getchar());
	for(;c>='0'&&c<='9';s=s*10+c-'0',c=getchar());
	return w*s;
}
int c,T,n,a[N],p[30],p2[30];
int qp(int a,int b){
	int ans=1;
	while(b){
		if(b&1) ans=1ll*ans*a%mod;
		b>>=1,a=1ll*a*a%mod;
	}
	return ans;
}
struct Val{
	int x,k;
	Val() : x(0),k(0) {}
	Val(int _x,int _k) : x(_x),k(_k) {}
}f[N],g[N],h[N],F[N]; 
Val operator * (const Val &u,const Val &v){return Val(1ll*u.x*v.x%mod,u.k+v.k);}
Val operator + (const Val &u,const Val &v){
	if(u.k==v.k) return Val((u.x+v.x)%mod,u.k);
	return u.k<v.k?u:v;
}
Val operator - (const Val &u,const Val &v){
	if(u.k==v.k) return Val((u.x-v.x+mod)%mod,u.k);
	return u.k<v.k?u:v;
}
void OR(Val *a,int t){
	for(int i=1;i<(1<<n);i<<=1){
		for(int len=i<<1,j=0;j<(1<<n);j+=len){
			for(int k=0;k<i;k++){
				if(t==1) a[j+k+i]=a[j+k+i]+a[j+k];
				else a[j+k+i]=a[j+k+i]-a[j+k];
			}
		}
	}
}	
signed main(){
	p[0]=p2[0]=1;
	for(int i=1;i<=25;i++) p[i]=(mod-2*p[i-1]%mod)%mod,p2[i]=1ll*p2[i-1]*inv2%mod;
	c=read(),T=read();
	while(T--){
		n=read();
		for(int i=0;i<(1<<n);i++){
			a[i]=read();
			f[i]=(a[i]+1==mod)?Val(1,1):Val(a[i]+1,0);
			g[i]=(2*a[i]+1==mod)?Val(1,1):Val((2*a[i]+1)%mod,0);
		}
		for(int i=0;i<n;i++){
			for(int s=0;s<(1<<n);s++){
				if(!(s>>i&1)) f[s]=f[s]*f[s|(1<<i)],g[s]=g[s]*g[s|(1<<i)];
			}
		}
		for(int s=0;s<(1<<n);s++) F[s]=Val(1ll*f[s].x*p[__builtin_popcount(s)]%mod,f[s].k);
		OR(F,1);
		for(int s=0;s<(1<<n);s++) h[s]=F[s]*F[s];
		OR(h,-1);
		int ans=0;
		for(int s=0;s<(1<<n);s++){
			Val t1=h[s]*g[s],t2=f[s]*f[s];
			if(t1.k==t2.k) (ans+=1ll*t1.x*qp(t2.x,mod-2)%mod*p2[__builtin_popcount(s)]%mod)%=mod;
		}
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2025-10-04 10:23  Green&White  阅读(4)  评论(0)    收藏  举报