题解:P13275 [NOI2025] 集合
masterhuang · · 题解
NOI 2025 折戟沉沙!
给定
定义全集
定义
对
部分分:保证
本题做法来自国家集训队 rk.9 hhoppitree,太大神!
思考:考虑
于是有经典容斥:
带入:
枚举
处理
对
定义
于是这部分答案为:
做点小变换
注意到
若没有
若存在
注意到经典结论,
于是我们在或卷积的时候顺便记录
由于只需要最低次,合并是
代码很短。
// 洛谷 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;
}