【LOJ#570】Misaka Network 与任务

Description

https://loj.ac/problem/570

Solution

\(C_i\)表示二进制数等于\(i\)的人数,再设\(G^k_i\)表示\(k\)个人\(and\)起来等于\(i\)的方案数。

考虑从易入手,\(k=2\)时的方案数式子是这样:\(G^2_i=\sum_{j\ and\ k=i}C_j\times C_k\)
这就是很经典的一个集合\(and\)卷积,考虑用FWT解决(这题其实就是\(C\)卷自己\(k\)次),求出\(FWT(C)\)后对位求\(k\)次幂即可。

关于FWT(\(and\)运算):

\(A\)\(B\)\(2^n\)次方维的向量,\(A+B\)\(A\cdot B\)满足向量运算规则,特殊定义一下\(A\ and\ B\)为对位求\(and\)卷积,即\(\{\sum_{i\ and\ j=x}A_i\times B_j\}(x\in [0,2^n))\)
\(A_0,A_1\)\(A\)前后\(2^{n-1}\)位,\(B_0,B_1\)同理。
定义变换:
\[FWT(A) = \begin{cases} (FWT(A_0+A_1),FWT(A_1)) & n>0 \\ A & n=0 \end{cases}\]
我们有\(FWT(A+B)=FWT(A)+FWT(B)\),证明大概是\(FWT(A)\)中每一项由\(A\)线性变换,还有\(FWT(A\ and\ B)=FWT(A)\cdot FWT(B)\),证明如下(以下\(and\)略去,\(FWT\)简写为\(F\)):
\begin{aligned}
F(AB)=&F(A_0B_0+A_0B_1+A_1B_0,A_1B_1)\=&(F(A_0B_0+A_0B_1+A_1B_0+A_1B_1),F(A_1)\cdot F(B_1)))
\=&((F(A_0)+F(A_1))\cdot(F(B_0)+F(B_1)),F(A_1)\cdot F(B_1))\=&(F(A_0+A_1),F(A_1))\cdot(F(B_0+B_1),F(B_1))\=&F(A)\cdot F(B)\end{aligned}
那么我们就可以开始变换了。

关于逆变换:

\(IFWT(A)\)\(A\)逆变换回去的向量(以下用\(FWT\)简写为\(F\)\(IWFT\)简写为\(f\)):
\begin{aligned}
f(F(A))=&f(F(A_0+A_1),F(A_1))\=&f(F(A_0)+F(A_1),F(A_1))\end{aligned}
那我们就可以构造一个逆变换\(f(A)=(f(A_0)-f(A_1),f(A_1))\),就可以变换回去了。

这题的具体实现就是求一下\(FWT(C)\),记为\(C'\),然后\(G'_i=C'^k_i\),最后\(G=IFWT(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);
}

猜你喜欢

转载自www.cnblogs.com/sadstone/p/9637702.html