题解:P13275 [NOI 2025] 集合
IvanZhang2009 · · 题解
赛时完全不知道怎么做,把三方暴力 dp 的
考虑这个特殊性质 B,大概就是让你把系数凑成
考虑随机找个方向容斥。第一个想法是对着
记
考虑尝试对着
我们直接容斥
考虑对于“至少存在一个”继续容斥。枚举
写出来相当于:
这里
假设你以及枚举了
这里“如果上述不满足”的限制比较唐。在性质 B 中
预处理
考虑优化也很容易,如果先枚举
如果先枚举
注意到人类智慧。我们的贡献只和
记
注意到
但是这里
其实很好做,我们用
这种题代码都很好写。
#include<bits/stdc++.h>
#define MOD 998244353
#define int long long
#define REP(i,a,n) for(int i=a;i<(int)(n);++i)
#define pb push_back
#define all(v) v.begin(),v.end()
#define pii pair<int,int>
#define cntbit(x) __builtin_popcount(x)
using namespace std;
int qpow(int x,int y){
int res=1;
while(y)res=y&1? res*x%MOD:res,x=x*x%MOD,y>>=1;
return res;
}
int ID;
struct number{
int x;
int c;
void operator =(int y){
if(y%MOD==0)x=1,c=1;
else x=y,c=0;
}
void operator *=(number a){
(x*=a.x)%=MOD;
(c+=a.c)%=MOD;
}
void operator *=(int a){
(x*=a)%=MOD;
}
void operator +=(number a){
int r=min(c,a.c);
x=((c==r)*x+(a.c==r)*a.x)%MOD,c=r;
}
void operator -=(number a){
int r=min(c,a.c);
x=((c==r)*x-(a.c==r)*a.x)%MOD,c=r;
}
};
int n;
int a[1100000];
number f[1100000],g[1100000];
void Main() {
cin>>n;
REP(i,0,(1<<n))cin>>a[i];
REP(i,0,(1<<n))f[i]=a[i]+1;
REP(i,0,n){
for(int j=(1<<n)-1;j>=0;--j)if(!((j>>i)&1)){
f[j]*=f[j|(1<<i)];
}
}
REP(i,0,(1<<n))f[i]*=qpow(-2,cntbit(i));
REP(j,0,n){
REP(i,0,(1<<n))if((i>>j)&1){
f[i]+=f[i^(1<<j)];
}
}
REP(i,0,(1<<n)){
f[i]*=f[i];
}
REP(j,0,n){
for(int i=(1<<n)-1;i>=0;--i)if((i>>j)&1){
f[i]-=f[i^(1<<j)];
}
}
REP(i,0,(1<<n)){
if(a[i]==MOD-1){
g[i]={a[i],-2};
}else g[i]=(a[i]*2+1)*qpow(a[i]+1,(MOD-2)*2)%MOD;
}
REP(i,0,n){
for(int j=(1<<n)-1;j>=0;--j)if(!((j>>i)&1)){
g[j]*=g[j|(1<<i)];
}
}
int ans=0;
REP(i,0,(1<<n)){
f[i]*=g[i];
if(f[i].c)continue;
int co=qpow((MOD+1)/2,cntbit(i))*f[i].x;
(ans+=co)%=MOD;
}
(ans+=MOD)%=MOD;
cout<<ans<<'\n';
}
signed main(){
int tc;
cin>>ID>>tc;
while(tc--)Main();
return 0;
}