Codeforces
1034E
题意
给你两个 \(2^n\) 的数组 \(a_0,..,a_{2^n-1}\) 和 \(b_0,..,b_{2^n-1}\)
在模 \(4\) 意义下求子集卷积
\(n\le 21\)
做法
显然有 \(\mathcal O(n^2 * 2^n)\)
的暴力子集卷积做法
令 \(A_{S,k}\) 表示对于 \(a\),集合为 \(S\),集合大小是 \(k\) 的值,\(B_{S,k}\) 同理
只有 \(A_{S,|S|}=a_S\)
是有效的,其余位置都是无效的
通过限制集合大小来保证转化为集合并卷积之后不会多算
\[f_{X,k}=\sum_{S\cup T=X} \sum_{i+j=k}
A_{S,i}B_{T,j}\]
答案就是每个 \(f_{S,|S|}\)
把第二维看成一个形式幂级数,就有
\[f_X(x)=\sum_{S\cup T=X} A_S(x)
B_T(x)\]
答案就是 \([x^{|S|}]f_S(x)\)
形式幂级数的加法是 \(\mathcal O(n)\)
的,乘法是 \(\mathcal O(n^2)\)
的,FMT是 \(\mathcal O(n * 2^n)\)
的,总复杂度 \(\mathcal O(n^2 *
2^n)\),无法通过此题
由于这里模数特殊,考虑把系数压到一个unsigned long long
中处理,两位恰好存一个系数
于是我们可以 \(\mathcal O(1)\)
地完成加法和乘法,总复杂度 \(\mathcal O(n *
2^n)\),可以通过此题
可能会有进位,但是通过分类处理加法和乘法还是可以完成的
事实上这里并不需要考虑进位的影响,因为 \([x^i]A_S(x)\) 和 \([x^j]B_T\) 会贡献到 \([x^{i+j}]f_{S\cup T}\),这里总是有 \(i+j\ge |S\cup T|\),我们需要的是 \([x^{|S\cup T|}]f_{S\cup
T}\),进位是不会对最低位产生影响的
所以直接+
和*
就好了
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<cstdio> #include<algorithm> #include<ctype.h> #include<string.h> #include<math.h>
using namespace std; #define ull unsigned long long
inline char read() { static const int IN_LEN = 1000000; static char buf[IN_LEN], *s, *t; return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++); } template<class T> inline void read(T &x) { static bool iosig; static char c; for (iosig=false, c=read(); !isdigit(c); c=read()) { if (c == '-') iosig=true; if (c == -1) return; } for (x=0; isdigit(c); c=read()) x=((x+(x<<2))<<1)+(c^'0'); if (iosig) x=-x; } const int OUT_LEN = 1<<21; char obuf[OUT_LEN], *ooh=obuf; inline void print(char c) { if (ooh==obuf+OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), ooh=obuf; *ooh++=c; } inline void flush() { fwrite(obuf, 1, ooh - obuf, stdout); }
const int N = 1<<21, M = 22; int n, cnt[N]; ull a[N], b[N]; int main() { read(n); for(int i=1; i<1<<n; ++i) cnt[i]=cnt[i^(i&-i)]+2; char x; while(isspace(x=read())); for(int i=0; i<1<<n; ++i) a[i]=(ull)(x-'0')<<cnt[i], x=read(); while(isspace(x=read())); for(int i=0; i<1<<n; ++i) b[i]=(ull)(x-'0')<<cnt[i], x=read();
for(int i=0; i<n; ++i) for(int j=0; j<1<<n; ++j) if(j>>i&1) a[j]+=a[j^(1<<i)], b[j]+=b[j^(1<<i)]; for(int i=0; i<1<<n; ++i) a[i]*=b[i]; for(int i=0; i<n; ++i) for(int j=0; j<1<<n; ++j) if(j>>i&1) a[j]-=a[j^(1<<i)]; for(int i=0; i<1<<n; ++i) print((char)('0'+(a[i]>>cnt[i]&3))); return flush(), 0; }
|