• Evaluation


    题目

    给出长度为(2^n) ((nle20))的数组(f)(g),求 (sum H(a)*(a+1) mod 2^{32})数组,定义为

    [H(a)={ sum}_{b|c=a}f[b]*g[c] ]

    分析

    好题,学到了很多东西。
    看懂题之后就想到zwl之前学过的FWT,即快速沃尔什变换,比赛的时候不会写。于是就开始想优化枚举的方法。暴力直接枚举(b)(c)(O(2^{2n})) 的,肯定会超时,不过最后没有想出更好的方法。本来预计是65左右的,结果只有35,原因是long long不知道怎么没有取好模。这里学到了,这种(q=2^{32})的取模可以直接用unsigned int自动溢出解决,优化常数。

    解法

    解法一 容斥原理

    [egin{aligned} H(a)&=sum_{b|c=a}f[b]*g[c] \ &=sum_{bigcup csubseteq a}f[b]*g[c] - sum _ {bigcup csubsetneq a} f[b]*g[c] \ &=sum_{bsubset a}f[b]* sum_{csubset a}g[c]-sum_{dsubsetneq a}H(d) end{aligned} ]

    于是问题转化成了求(f)(g)的子集前缀和,即所有子集的(f)(g)的和。
    所以我们从小到大枚举所有集合,后面枚举到的集合的子集都已经算好了,所以加上自己就好。最后同样从小到大枚举集合,后面枚举到的(H)减去前面的,即减去真子集。这里的或运算转化很漂亮,代码也很简洁优雅。

    代码

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    typedef unsigned int giant;
    giant read() {
    	giant x=0,f=1;
    	register char c=getchar();
    	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*f;
    }
    const giant maxn=(1<<20)+100;
    giant f[maxn],g[maxn],h[maxn];
    int main() {
    	#ifndef ONLINE_JUDGE
    		freopen("test.in","r",stdin);
    		freopen("my.out","w",stdout);
    	#endif
    	giant n=read();
    	giant s=1<<n;
    	for (register giant i=0;i<s;++i) f[i]=read();
    	for (register giant i=0;i<s;++i) g[i]=read();
    	for (register giant j=1;j<s;j<<=1) for (int i=0;i<s;++i) if (i&j) f[i]+=f[j^i],g[i]+=g[j^i];
    	for (register giant i=0;i<s;++i) h[i]=f[i]*g[i];
    	for (register giant j=1;j<s;j<<=1) for (int i=0;i<s;++i) if (i&j) h[i]-=h[i^j];
    	giant ans=0;
    	for (register giant i=0;i<s;++i) ans+=h[i]*(i+1);
    	printf("%u
    ",ans);
    }
    

    解法二 快速沃尔什变换

    快速沃尔什变换解决的是这样一类问题:

    [{C_i}={ sum} _ {j igoplus k=i} A_j *B_ k ]

    其中数组长度为(2^n).
    与FFT相似,我们先把系数表达转为点值,相乘后转回来。
    先以$igoplus = or $为例。
    我们可以把一个数组拆分为相同长度的两段:

    [A=(A_0,A_1) ]

    其中

    [A_0=A[0....2^{n-1}],A_1=A[2^{n-1}+1...2^n-1] ]

    即二进制最高位为0或1.
    (i)的第(n)位为(0),则有两种可能:

    [0 or 0 = 0 ]

    (i)的第(n)位为(1),则有两种可能:

    [0 or 1 = 1 \ 1 or 0 = 1 \ 1 or 1=1 ]

    能够产生1的放后面,产生0的放前面,通过位运算的性质,我们可推出or运算的转换方法,相乘之后推一下是对的:

    [For=(For(A_0),For(A_0+A_1)) \ Ior=(Ior(A_0),Ior(A_1-A_0)) \ ]

    验证:

    [egin{aligned} A=(A_0,A_1) \ Aprime =(A_0,A_0+A_1) \ B=(B_0,B_1) \ Bprime =(B_0,B_0+B_1) \ C=A*B=(A_0*B_0,A_0*B_1+A_1*B_0+A_1*B_1) \ Cprime =Aprime * Bprime=(A_0*B_0,A_0*B_0+A_0*B_1+A_1*B_0+A_1*B_1) \ 若 Cprime =(D,E),则观察发现C=(D,E-D),与上面的公式相符。 end{aligned} ]

    其他的转换方法:

    [Fand=(Fand(A_0+A_1),Fand(A_1)) \ Iand=(Iand(A_1-A_0),Iand(A_1)) \ Fxor=(Fxor(A_0+A_1),Fxor(A_0-A_1)) \ Ixor=(Ixor(frac{A_0+A_1}{2}),Ixor(frac{A_0-A_1}{2})) ]

    所以这道题是模板题,写法和(fft)差不多,不需要(ntt)的原根或者复数根之类的,只是类似模拟一下过程而已。

    代码

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    typedef unsigned int giant;
    giant read() {
    	giant x=0,f=1;
    	char c=getchar();
    	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*f;
    }
    const giant maxn=(1<<20)+1;
    giant a[maxn],b[maxn],n,m,f[maxn];
    void F(giant a[],bool inv=false) {
    	for (register giant i=0;i<m;++i) if (i<f[i]) swap(a[i],a[f[i]]);
    	for (register giant i=2;i<=m;i<<=1) {
    		for (register giant j=0;j<m;j+=i) {
    			for (register giant k=j;k<j+i/2;++k) {
    				giant u=a[k],v=a[k+i/2];
    				a[k+i/2]=(inv?v-u:u+v);
    			}	
    		}
    	}
    }
    int main() {
    	#ifndef ONLINE_JUDGE
    		freopen("test.in","r",stdin);
    	#endif
    	m=1<<(n=read());
    	for (register giant i=0;i<m;++i) f[i]=(f[i>>1]>>1)|((i&1)<<(n-1));
    	for (register giant i=0;i<m;++i) a[i]=read();
    	for (register giant i=0;i<m;++i) b[i]=read();
    	F(a);
    	F(b);
    	for (register giant i=0;i<m;++i) a[i]*=b[i];
    	F(a,true);
    	giant ans=0;
    	for (register giant i=0;i<m;++i) ans+=a[i]*(i+1);
    	printf("%u
    ",ans);
    }
    
  • 相关阅读:
    bbb SOCKET CAN
    在BBB上使用CCS5开发环境
    BBB的PRU模块
    垃圾邮件分类
    yzoj 2377 颂芬梭哈 题解
    yzoj 2372 小B的数字 题解
    yzoj P2371 爬山 题解
    hdu 1007 Quoit Design 题解
    手写堆
    yzoj P1126 塔 题解
  • 原文地址:https://www.cnblogs.com/owenyu/p/6724748.html
Copyright © 2020-2023  润新知