zoukankan      html  css  js  c++  java
  • [CF1034E]Little C Loves 3 III(不交并卷积FWT讲解)

    题面

    http://codeforces.com/contest/1034/problem/E

    题解

    前置知识

    不交并卷积

    是可以使用FWT进行优化的一种卷积形式。

    [c{[}k{]}=sumlimits_{i|j = x,i{&}j=0}a[i]b[j] ]

    解决方案:设(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进行或卷积后得到。举个例子:

    [a[0]=0,a[1]=1,a[2]=2,a[3]=3 ]

    [b[0]=3,b[1]=2,b[2]=1,b[3]=0 ]

    那么就有

    [A[0]=0,A[1]=x,A[2]=2x,A[3]=3x^2 ]

    [B[0]=3,B[1]=2x,B[2]=x,B[3]=0x^2 ]

    注意,这里的x与生成函数中的作用相同,只是用来表示多项式形式的参数。

    (A,B)两个多项式数组进行或卷积后得到

    [C[0]=0,C[1]=3x+2x^2,C[2]=6x+2x^2,C[3]=14x^2+9x^3 ]

    然后我们对于每一个C[k],把它中间(x^{pop{\_}cnt[k]})前面的系数拿出来

    [c[0]=0,c[1]=3,c[2]=6,c[3]=14 ]

    就得到了不交并卷积的答案!

    这一过程的原理是这样的,如果(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实际上是

    [C[k]=sumlimits_{i|j=k}A[i]B[j] ]

    [=sumlimits_{i|j=k}a[i]b[j]x^{pop{\_}cnt[i]+pop{\_}cnt[j]} ]

    取出了(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为例:

    [a[0]=0,a[1]=1,a[2]=2,a[3]=3 ]

    [b[0]=3,b[1]=2,b[2]=1,b[3]=0 ]

    [A[0]=0,A[1]=x=4,A[2]=2x=8,A[3]=3x^2=48 ]

    [B[0]=3,B[1]=2x=8,B[2]=x=4,B[3]=0x^2=0 ]

    [C[0]=0,C[1]=3x+2x^2=44,C[2]=6x+2x^2=56,C[3]=14x^2+9x^3=800 ]

    然后令(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代入后的值

    [A[0]=0,A[1]=4,A[2]=8,A[3]=48 ]

    [B[0]=3,B[1]=8,B[2]=4,B[3]=0 ]

    就可以了,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;
    }
    
  • 相关阅读:
    mysql学习笔记-数据库相关操作
    bugku-flag在index里(本地文件包含漏洞+php伪协议的结合应用)
    nmap的指令学习
    雅礼2018-03-11
    两道题,雅礼一题矩阵转置
    2018-03-05 计算鞍点
    多维数组
    TCP拥塞控制
    TCP三次握手和四次挥手
    MySQL事务隔离级别详解
  • 原文地址:https://www.cnblogs.com/xh092113/p/12390885.html
Copyright © 2011-2022 走看看