NOI2025 D2T2 集合 (set) 题解
给定 \(n,a_0,a_1,\cdots,a_{2^n-1}\),定义数的 \(\sube\) 表示二进制属于,集合的属于记号保持原意。
定义全集 \(U=\{0,1,\cdots,2^n-1\},\forall S\sube U\) 定义 \(f(S)=\mathop{\text{AND}}\limits_{x\in S}\,x\),特别的 \(f(\varnothing)=2^n-1\)。
定义 \(\pi(S)=\prod\limits_{x\in S}a_x,\pi(\varnothing)=1\)。
对 \(p=998244353\) 取模,\(2\le n\le 20,a_i\in [0,p)\)。
部分分:保证 \(a_i\ne p-1\)。
本题做法来自国家集训队 rk.9 hhoppitree,太大神!
思考:考虑 \(f\) 的相等肯定是无法刻画的,我们容易刻画的是 \(x\sube f(S)\Leftrightarrow S\sube S_x\),其中超集 \(S_x=\{y:x\sube y\}\)。
于是有经典容斥:\([P=Q]=\sum\limits_{A\sube P,B\sube Q} (-1)^{|A|+|B|}2^{|A\cap B|}\)。
带入:
枚举 \(C=A\cap B\):
处理 \(\sum\limits_{S\sube S_{A\cup C},T\sube S_{B\cup C}} [S\cap T=\varnothing]\pi(S\cup T)\) 这个东西的常见手段是:
对 \(S_{A\cup C}\cup S_{B\cup C}\) 中的每个元素单独考虑放在 \(S\) 还是 \(T\) 还是丢弃。
定义 \(L=A\cup B\cup C,D=S_{A\cup C}-S_L,E=S_{B\cup C}-S_L\)。
-
\(i\in L\) 能放在 \(S,T\) 或不放
-
\(i\in D\) 能放在 \(S\) 或不放
-
\(i\in E\) 能放在 \(T\) 或不放
于是这部分答案为:
做点小变换 \((-1)^{|A|+|B|}=(-1)^{|A\cup C|}\cdot(-1)^{|B\cup C|},2^{|C|}=2^{|A\cup C|+|B\cup C|-|A\cup B\cup C|}\) 后有:
注意到 \(A\cup B\cup C=(A\cup C)\cup (B\cup C)\),就是一个或卷积的形式,有:
若没有 \(a_i=p-1\) 则 \(f,g\) 直接预处理,复杂度 \(\mathcal{O}(n2^n)\)。
若存在 \(a_i=p-1\),我们如下变换:\((1+a_i)\to x,\dfrac{1+2a_i}{(1+a_i)^2}\to\dfrac{2x-1}{x^2}\),其中 \(x \to 0\)。
注意到经典结论,\(\lim\limits_{x\to 0}\frac{f(x)}{g(x)}\) 是两个多项式最低次项的比值。
于是我们在或卷积的时候顺便记录 \(x\) 项造成的多项式分数的最低次项,合并直接合并即可。
由于只需要最低次,合并是 \(\mathcal{O}(1)\) 的,复杂度 \(\mathcal{O}(n2^n)\)。
代码很短。
// 洛谷 P13275
// https://www.luogu.com.cn/problem/P13275
#include<bits/stdc++.h>
#define LL long long
#define P pair<int,int>
#define fi first
#define se second
#define pc __builtin_popcount
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
inline int rd()
{
int x=0;char c=getchar();
for(;c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
return x;
}
const int N=1<<20|5,mod=998244353,I2=(mod+1)>>1,_2=mod-2;
int C,n,m,a[N],p[23],q[23],ans;P f[N],g[N];
inline void ad(int &x,int y){x+=y;(x>=mod)&&(x-=mod);}
inline int ksm(int x,int p=mod-2){int s=1;for(;p;(p&1)&&(s=1ll*s*x%mod),x=1ll*x*x%mod,p>>=1);return s;}
inline P R(P A){return {A.fi,mod-A.se};}
inline void operator+=(P &A,P B){
if(A>B) swap(A,B);
if(A.fi==B.fi) ad(A.se,B.se);
}
inline void operator*=(P &A,P B){A={A.fi+B.fi,1ll*A.se*B.se%mod};}
inline void operator*=(P &A,int t){A={A.fi,1ll*A.se*t%mod};}
int main()
{
C=rd(),C=rd();
for(int i=*p=*q=1;i<=20;i++) p[i]=1ll*I2*p[i-1]%mod,q[i]=1ll*_2*q[i-1]%mod;
while(C--)
{
n=rd();m=1<<n;ans=0;
for(int i=0;i<m;i++) a[i]=rd();
for(int i=0,x;i<m;i++)
{
if(a[i]==mod-1) f[i]={1,1},g[i]={-2,mod-1};
else f[i]={0,x=1+a[i]},x=ksm(x),g[i]={0,(1+2ll*a[i])*x%mod*x%mod};
}
for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(j>>i&1)
f[j^(1<<i)]*=f[j],g[j^(1<<i)]*=g[j];
for(int i=0;i<m;i++) f[i]*=q[pc(i)],g[i]*=p[pc(i)];
for(int i=0;i<n;i++) for(int j=0;j<m;j++)
if(j>>i&1) f[j]+=f[j^(1<<i)];
for(int i=0;i<m;i++) f[i]*=f[i];
for(int i=0;i<n;i++) for(int j=0;j<m;j++)
if(j>>i&1) f[j]+=R(f[j^(1<<i)]);
for(int i=0;i<m;i++)
{
f[i]*=g[i];
if(!f[i].fi) ad(ans,f[i].se);
}
cout<<ans<<"\n";
}
return 0;
}

浙公网安备 33010602011771号