【LOJ#570】Misaka Network 与任务
Description
Solution
设 Ci C i 表示二进制数等于 i i 的人数,再设表示 k k 个人起来等于 i i 的方案数。
考虑从易入手,时的方案数式子是这样:
G2i=∑j and k=iCj×Ck
G
i
2
=
∑
j
a
n
d
k
=
i
C
j
×
C
k
。
这就是很经典的一个集合
and
a
n
d
卷积,考虑用FWT解决(这题其实就是
C
C
卷自己次),求出
FWT(C)
F
W
T
(
C
)
后对位求
k
k
次幂即可。
关于FWT(运算):
设
A
A
,为
2n
2
n
次方维的向量,
A+B
A
+
B
、
A⋅B
A
⋅
B
满足向量运算规则,特殊定义一下
A and B
A
a
n
d
B
为对位求
and
a
n
d
卷积,即
{∑i and j=xAi×Bj}(x∈[0,2n))
{
∑
i
a
n
d
j
=
x
A
i
×
B
j
}
(
x
∈
[
0
,
2
n
)
)
。
设
A0,A1
A
0
,
A
1
为
A
A
前后位,
B0,B1
B
0
,
B
1
同理。
定义变换:
我们有 FWT(A+B)=FWT(A)+FWT(B) F W T ( A + B ) = F W T ( A ) + F W T ( B ) ,证明大概是 FWT(A) F W T ( A ) 中每一项由 A A 线性变换,还有,证明如下(以下 and a n d 略去, FWT F W T 简写为 F F ):
那么我们就可以开始变换了。
关于逆变换:
设
IFWT(A)
I
F
W
T
(
A
)
为
A
A
逆变换回去的向量(以下用简写为
F
F
,简写为
f
f
):
那我们就可以构造一个逆变换 f(A)=(f(A0)−f(A1),f(A1)) f ( A ) = ( f ( A 0 ) − f ( A 1 ) , f ( A 1 ) ) ,就可以变换回去了。
这题的具体实现就是求一下 FWT(C) F W T ( C ) ,记为 C′ C ′ ,然后 G′i=C′ki G i ′ = C i ′ k ,最后 G=IFWT(G′) G = I F W T ( G ′ ) 。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef long long ll;
const int M=5e6+10,mo=1e9+7;
int a[M];
int read(){
char ch=' ';int t=0;
for(;ch<'0' || ch>'9';ch=getchar());
for(;ch>='0' && ch<='9';ch=getchar()) t=(t<<1)+(t<<3)+ch-48;
return t;
}
ll pow(ll x,int y){
ll b=1;
for(;y;y>>=1,x=x*x%mo) if(y&1) b=b*x%mo;
return b;
}
void FWT(int *f,int n,int sig){
for(int m=2;m<=n;m<<=1){
int half=m>>1;
for(int i=0;i<n;i+=m)
fo(j,i,i+half-1) a[j]=(a[j]+a[j+half]*sig+mo)%mo;
}
}
int main()
{
int n=read(),m=read(),k=read();
fo(i,1,m) a[read()]++;
FWT(a,1<<n,1);
fo(i,0,(1<<n)-1) a[i]=pow(a[i],k);
FWT(a,1<<n,-1);
ll ans=0;
fo(i,1,(1<<n)-1) ans=(ans+a[i])%mo;
printf("%lld\n",ans);
}