zoukankan      html  css  js  c++  java
  • [集训]FWT基础练习题

    题意

    给出n个长度为20的二进制数和数字k,每次询问给出一个二进制数,问从n个数中挑k个数(不能重复)的按位或能包含询问的组合有多少个。数字均小于等于5E5,1s。


    思考

    强行算出2^20个答案,再O(1)询问。

    可知按位或的FWT能够将两个数组融合成新的数组。假设Fk表示挑出k个数字能组成的所有可能的,A为原始的桶数组,可知Fk[i]=(Fk-1 或运算FWT A)[i]-(k-1)Fk-1[i],后面减法的目的是减去数被重复选择的部分。

    再观察FWT其实是线性变换,因此Fk[i]可以预先乘上k方便下一次计算。因此最开始只要乘上某个组合数即可。

    最后根据按位与FWT可得到所有的答案。总复杂度O(nlogn)。


    代码

     1 #pragma GCC optimize 2
     2 #include<bits/stdc++.h>
     3 #define mod 1000000007
     4 using namespace std;
     5 typedef long long int ll;
     6 const int maxn=1E6+5E5+5;
     7 const int LEN=4;
     8 const int G=1<<LEN;
     9 ll n,k,m,a[maxn],b[maxn],c[maxn],d[maxn];
    10 ll fac[maxn],inv[maxn];
    11 string str;
    12 inline int get(string str)
    13 {
    14     int sum=0;
    15     for(int i=0;i<LEN;++i)
    16         sum=sum*2+str[i]-'0';
    17     return sum;
    18 }
    19 ll qpow(ll x,ll y)
    20 {
    21     ll ans=1,base=x;
    22     while(y)
    23     {
    24         if(y&1)
    25             ans=ans*base%mod;
    26         base=base*base%mod;
    27         y>>=1;
    28     }
    29     return ans;
    30 }
    31 void init()
    32 {
    33     fac[0]=1;
    34     for(int i=1;i<=500000;++i)
    35         fac[i]=fac[i-1]*i%mod;
    36     inv[500000]=qpow(fac[500000],mod-2);
    37     for(int i=500000-1;i>=0;--i)
    38         inv[i]=inv[i+1]*(i+1)%mod;
    39 }
    40 void FWT(ll*A,int t1,int t2)
    41 {
    42     for(int len=2;len<=G;len<<=1)
    43         for(int i=0;i<G/len;++i)
    44             for(int j=0;j<len/2;++j)
    45             {
    46                 ll a=A[i*len+j],b=A[i*len+j+len/2];
    47                 A[i*len+j]=(a+b*t1)%mod;
    48                 A[i*len+j+len/2]=(b+a*t2)%mod;
    49             }
    50 }
    51 template<class T>void copy(T*a,T*b,int x)
    52 {
    53     for(int i=0;i<x;++i)
    54         a[i]=b[i];
    55 }
    56 int main()
    57 {
    58     ios::sync_with_stdio(false);
    59     init();
    60     cin>>n>>k>>m;
    61     for(int i=1;i<=n;++i)
    62     {
    63         cin>>str;
    64         ++a[get(str)];
    65     }
    66     copy(c,a,G);
    67     FWT(a,0,1);
    68     for(int i=0;i<G;++i)
    69     {
    70         if(a[i]>=k)
    71             a[i]=fac[a[i]]*inv[a[i]-k]%mod;
    72         else
    73             a[i]=0;
    74     }
    75     FWT(a,0,-1);
    76     /*
    77     for(int i=2;i<=k;++i)
    78     {
    79         copy(d,a,G);
    80         FWT(a,0,1);
    81         copy(b,c,G);
    82         FWT(b,0,1);
    83         for(int j=0;j<G;++j)
    84             a[j]=a[j]*b[j]%mod;
    85         FWT(a,0,-1);
    86         for(int j=0;j<G;++j)
    87             a[j]=(a[j]-d[j]*(i-1)+mod)%mod;
    88     }
    89     */
    90     FWT(a,1,0);
    91     while(m--)
    92     {
    93         cin>>str;
    94         cout<<a[get(str)]*inv[k]%mod<<endl;
    95     }
    96     return 0;
    97 }
    View Code
  • 相关阅读:
    复杂链表的复制
    [CSP-S模拟测试]:抽卡(概率DP)
    [CSP-S模拟测试]:计划(前缀和)
    [CSP-S模拟测试]:公园(BFS+剪枝)
    [CSP-S模拟测试]:长寿花(DP+组合数)
    [CSP-S模拟测试]:喝喝喝(模拟)
    [CSP-S模拟测试]:次芝麻(数学)
    [CSP-S模拟测试]:赤壁情(DP)
    [CSP-S模拟测试]:密州盛宴(贪心)
    [CSP-S模拟测试]:春思(数学)
  • 原文地址:https://www.cnblogs.com/GreenDuck/p/11104630.html
Copyright © 2011-2022 走看看