【LOJ#570】Misaka Network 与任务

Description

https://loj.ac/problem/570

Solution

Ci C i 表示二进制数等于 i i 的人数,再设Gik表示 k k 个人and起来等于 i i 的方案数。

考虑从易入手,k=2时的方案数式子是这样: 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 卷自己k次),求出 FWT(C) F W T ( C ) 后对位求 k k 次幂即可。

关于FWT(and运算):

A A B 2n 2 n 次方维的向量, A+B A + B AB 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 前后2n1位, B0,B1 B 0 , B 1 同理。
定义变换:

FWT(A)={(FWT(A0+A1),FWT(A1))An>0n=0 F W T ( A ) = { ( F W T ( A 0 + A 1 ) , F W T ( A 1 ) ) n > 0 A n = 0

我们有 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 线性变换,还有FWT(A and B)=FWT(A)FWT(B),证明如下(以下 and a n d 略去, FWT F W T 简写为 F F ):
F(AB)=F(A0B0+A0B1+A1B0,A1B1)=(F(A0B0+A0B1+A1B0+A1B1),F(A1)F(B1)))=((F(A0)+F(A1))(F(B0)+F(B1)),F(A1)F(B1))=(F(A0+A1),F(A1))(F(B0+B1),F(B1))=F(A)F(B)

那么我们就可以开始变换了。

关于逆变换:

IFWT(A) I F W T ( A ) A A 逆变换回去的向量(以下用FWT简写为 F F IWFT简写为 f f ):

f(F(A))=f(F(A0+A1),F(A1))=f(F(A0)+F(A1),F(A1))

那我们就可以构造一个逆变换 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 ′ ,然后 Gi=Cki 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);
}
posted @ 2018-09-12 22:06  sadstone  阅读(108)  评论(0编辑  收藏  举报