题面
http://codeforces.com/contest/1034/problem/E
题解
前置知识
不交并卷积
是可以使用FWT进行优化的一种卷积形式。
解决方案:设(A,B,C)是三个多项式数组,它们的每一项是一个多项式。其中(A[i]=a[i] x^{pop{\_}cnt[i]}),(B[i]=b[i]x^{pop{\_}cnt[i]})。(pop_cnt[i]是二进制表示下i中1的个数),而C数组是A与B进行或卷积后得到。举个例子:
那么就有
注意,这里的x与生成函数中的作用相同,只是用来表示多项式形式的参数。
将(A,B)两个多项式数组进行或卷积后得到
然后我们对于每一个C[k],把它中间(x^{pop{\_}cnt[k]})前面的系数拿出来
就得到了不交并卷积的答案!
这一过程的原理是这样的,如果(i|j=k),那么一定有(pop{\_}cnt[i]+pop{\_}cnt[j]{geq}pop{\_}cnt[k]),并且等号iff i&j=0。这很好理解,因为如果(i{&}j{ eq}0),那么(i{&}j)的每一位上的1都被重复计算了两遍。
我们计算出来的C实际上是
取出了(x^{pop{\_}cnt[k]})之前的系数,就自然得到了(sum_{i|j=k,i{&}j=0}a[i]b[j])啦。
这样的时间复杂度,假设a,b的长度是(2^n),FWT时原本是两个数相加减,现在变成了两个长度(O(n))的多项式相加。只有这里有别,所以总时间复杂度是(O(n^22^n))。
回原题
虽然本题求的就是不交并卷积,看上去像模板题,可是(O(n^22^n))的复杂度竟然过不去!这是因为此题的特殊性——要求对4取模,导致可以重新优化掉一个n。
在前面的过程中,我们把x=4代入,仍以样例2为例:
然后令(c[k]=C[k] div 4^{pop{\_}cnt[k]}),得到
(c[0]=0,c[1]=3+2x=11,c[2]=6+2x=14,c[3]=14+9x=50)
最后分别(mod 4),就可以得到答案{0,3,2,2}了。
由于我也保留了未代入前的式子,所以这么做的原理不难理解,我们想要求出C[k]中的(x^{pop{\_}cnt[k]})前的系数(mod 4)的值,就直接把C[k]除以(4^{pop{\_}cnt[k]})再直接(mod 4),次数更高的项仍然能被4整除,在mod的时候就不产生影响了。
这么做,就可以不用构造多项式数组,直接存x=4代入后的值
就可以了,FWT时还是两个数相加减,复杂度相应地回到了(O(n2^n))。
过程中,A,B进行或卷积得到的系数可能会很大,为了防止爆掉,采取一种措施——将所有的系数在(mod 4^{22})意义下进行。由于最终最多需要求系数(div 4^{21})以后对4取模的值,所以这样仍能保证答案的正确。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
#define rg register
#define In inline
const ll N = 2097152;
const ll mod = 1ll << 44;
namespace ModCalc{
In void Inc(ll &x,ll y){
x += y;if(x >= mod)x -= mod;
}
In void Dec(ll &x,ll y){
x -= y;if(x < 0)x += mod;
}
In ll Add(ll x,ll y){
Inc(x,y);return x;
}
In ll Sub(ll x,ll y){
Dec(x,y);return x;
}
In void Adjust(ll &x){
x = (x % mod + mod) % mod;
}
In void Tms(ll &x,ll y){
x = (ll)x * y - (ll)((ld)x * y / mod) * mod;
Adjust(x);
}
In ll Mul(ll x,ll y){
Tms(x,y);return x;
}
};
using namespace ModCalc;
ll n,deg;
char s[N+5];
ll a[N+5],b[N+5];
In void calc(ll &x,ll &y,ll opt){
if(opt == 1)Inc(y,x);
else Dec(y,x);
}
In void FWT(ll a[],ll deg,ll opt){
for(rg int n = 2;n <= deg;n <<= 1){
int m = n >> 1;
for(rg int i = 0;i < deg;i += n)
for(rg int j = 0;j < m;j++)calc(a[i+j],a[i+j+m],opt);
}
}
ll popc[N+5];
int main(){
scanf("%lld",&n);
deg = 1ll << n;
for(rg int i = 1;i < deg;i++)popc[i] = popc[i>>1] + (i&1);
scanf("%s",s);
for(rg int i = 0;i < deg;i++)a[i] = Mul(s[i] - '0',1ll << (popc[i]<<1));
scanf("%s",s);
for(rg int i = 0;i < deg;i++)b[i] = Mul(s[i] - '0',1ll << (popc[i]<<1));
FWT(a,deg,1);
FWT(b,deg,1);
for(rg int i = 0;i < deg;i++)Tms(a[i],b[i]);
FWT(a,deg,-1);
for(rg int i = 0;i < deg;i++)a[i] >>= (popc[i] << 1),a[i] &= 3;
for(rg int i = 0;i < deg;i++)putchar(a[i] + '0');putchar('
');
return 0;
}