【XSY2813】【LOJ6358】前夕(容斥)
题面
题解
神仙fl!/se
题意:有一个大小为 \(n\) 的集合,易知它有 \(2^n\) 个不同的子集,在这些子集中选出一些集合,使得他们的交集大小为 \(4\) 的倍数,求选的方案数。
首先我们设 \(f(i)\) 表示钦定交集大小至少为 \(i\) 时的方案数,易知:
减 \(1\) 是因为不能不选。
考虑构造容斥系数 \(\alpha(i)\) 使得答案为:
我们考虑一种选择的方案,设这种方案选出的集合的并集大小为 \(x\)。
一方面,它实际的贡献应该是 \([4|x]\)。
另一方面,我们考虑它在式子中的贡献。显然它只会在 \(0\leq i\leq x\) 的 \(f(i)\) 有贡献,那么它在 \(ans\) 中的贡献为:
那么可以列出方程:
根据单位根反演(\(f(n)=\sum\limits_{i=0}^n\dbinom{n}{i}g(i)\iff g(n)=\sum\limits_{i=0}^n(-1)^{n-i}\dbinom{n}{i}f(i)\)):
\([4|x]\) 不太好处理,如果它是个多项式就好了。
神奇的方法:单位根反演!
补充知识:单位根反演
定理:\(\forall k\geq 0,[n|k]=\dfrac{1}{n}\sum\limits_{i=0}^{n-1}w_{n}^{ik}\)。(其中 \(w\) 是单位根)
证明:
当 \(n|k\) 时,\(w_n^{ik}=1\),故右式值为 \(1\);
当不满足 \(n|k\) 时,\(\sum\limits_{i=0}^{n-1}w_{n}^{ik}\) 是等比数列求和的形式,值为 \(0\)。
于是:
注意到 \(\sum\limits_{i=0}^x(-1)^{x-i}\dbinom{x}{i}w_4^{ji}=\sum\limits_{i=0}^x\dbinom{x}{i}(-1)^{x-i}\left(w_4^{j}\right)^{i}=\left(-1+w_4^j\right)^x\)。
所以:
直接做即可,注意要严格 \(O(n)\)。
最后答案要加 \(1\),是因为你可以什么都不选。(注意“什么都不选”和“只选了空集”是两种不同的方案)
代码如下:
#include<bits/stdc++.h>
#define N 10000010
using namespace std;
namespace modular
{
const int mod=998244353;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
const int inv2=(mod+1)/2,inv4=mul(inv2,inv2);
}using namespace modular;
inline int poww(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n;
int facn,ifac[N];
int pow22[N];
int w[4];
void init()
{
facn=1;
for(int i=1;i<=n;i++) facn=mul(facn,i);
ifac[n]=poww(facn,mod-2);
for(int i=n;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
pow22[0]=2;
for(int i=1;i<=n;i++) pow22[i]=mul(pow22[i-1],pow22[i-1]);
int gn=poww(3,(mod-1)/4);
int g=1;
for(int i=0;i<4;g=mul(g,gn),i++) w[i]=g;
}
int Cn(int m)
{
return mul(mul(facn,ifac[m]),ifac[n-m]);
}
int main()
{
n=read();
init();
int now[4]={1,1,1,1},ans=0;
for(int i=0;i<=n;i++)
{
int a=0;
for(int j=0;j<4;j++)
{
a=add(a,now[j]);
now[j]=mul(now[j],dec(w[j],1));
}
a=mul(inv4,a);
int f=mul(Cn(i),dec(pow22[n-i],1));
ans=add(ans,mul(f,a));
}
printf("%d\n",add(ans,1));
return 0;
}