码迷,mamicode.com
首页 > 其他好文 > 详细

[集训]FWT基础练习题

时间:2019-06-28 20:59:49      阅读:121      评论:0      收藏:0      [点我收藏+]

标签:long   gif   turn   二进制   目的   观察   练习   合成   open   

题意

给出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

 

[集训]FWT基础练习题

标签:long   gif   turn   二进制   目的   观察   练习   合成   open   

原文地址:https://www.cnblogs.com/GreenDuck/p/11104630.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!