zoukankan      html  css  js  c++  java
  • Codeforces 1336E Chiori and Doll Picking (子集和变换、线性基、阈值算法、状压 DP、组合计数)

    题目链接

    https://codeforces.com/contest/1336/problem/E

    题解

    假设线性基大小是 (L),其异或值域记作 (S),则对于异或值域内每个数,显然有 (2^{n-L}) 种方案异或得到。因此只需要建一组线性基然后对这个线性基求答案即可,相当于 (nle m).
    数据分治。
    算法一:
    暴力枚举每个元素选不选。
    时间复杂度 (O(2^L)).
    算法二:
    记录非自由元的状态进行状压 DP.
    时间复杂度 (O(2^{m-L}m^2)).
    结合算法一和算法二,可以通过 E1 题。
    算法三:
    下面约定某个数组 FWT 后的数组用对应的大写字母表示。
    设长为 (2^m) 的数组 (b_k[i]=[ ext{bitcnt}(i)=k])(c[i]=[iin S])
    (k) 的答案为 (b_k)(c) 异或卷积后为 (0) 的位置上的值,即 (frac{1}{2^m}sum^{2^m-1}_{i=0}B_k[i]C[i]).
    根据 FWT 的定义,易得 (B_k[i]=sum^{2^m-1}_{j=0}(-1)^{ ext{bitcnt}(i& j)}[ ext{bitcnt}(j)=k]=sum^{ ext{bitcnt}(i)}_{j=0}(-1)^j{ ext{bitcnt}(i)choose j}{m- ext{bitcnt}(i)choose k-j}),即 (B_k[i]) 只和 ( ext{bitcnt}(i)) 有关,可以在关于 (m) 的多项式时间内求出。
    现在考虑如何求 (C): (C[i]=sum_{xin S} (-1)^{ ext{bitcnt}(i& x)}),不难观察到若存在线性基里的一个元素与 (i)( ext{and})( ext{bitcnt}) 为奇数,则该值一定为 (0)(映射法易证),否则为 (2^L). 那么只有那些与线性基里的每个元素 ( ext{and})( ext{bitcnt}) 都为偶数的 (i) 有用。而满足这个条件的 (i) 显然不能包含任何一个自由元,因此不超过 (2^{m-L}) 个,可以直接爆搜。要求的是和 (B_k) 点积后的结果,而 (B_k[i]) 的取值只和 ( ext{bitcnt(i)}) 有关,因此只需要对每个 ( ext{bitcnt}) 统计出 (C_i) 的和即可。
    时间复杂度 (O(2^{m-L}+m^3)).
    结合算法一和算法三,可以通过 E2 题。
    时间复杂度 (O(2^{frac{m}{2}}+m^3+nm)).

    算法三最难想的是第一步吧……异或的性质好神奇,Sooke 好强!

    代码

    #include<bits/stdc++.h>
    #define llong long long
    #define mkpr make_pair
    #define iter iterator
    #define riter reversed_iterator
    #define y1 Lorem_ipsum_dolor
    using namespace std;
     
    inline int read()
    {
    	int x = 0,f = 1; char ch = getchar();
    	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
    	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
    	return x*f;
    }
     
    const int mxN = 1<<19;
    const int mxM = 53;
    const int P = 998244353;
    llong comb[mxM+3][mxM+3];
    int bitcnt[mxN+3];
    llong a[mxM+3]; int b[mxM+3];
    llong ans[mxM+3];
    int n,m,sz;
     
    llong quickpow(llong x,llong y)
    {
    	llong cur = x,ret = 1ll;
    	for(int i=0; y; i++)
    	{
    		if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}
    		cur = cur*cur%P;
    	}
    	return ret;
    }
     
    void initfact(int n)
    {
    	comb[0][0] = 1ll;
    	for(int i=1; i<=n; i++) {comb[i][0] = comb[i][i] = 1ll; for(int j=1; j<i; j++) {comb[i][j] = (comb[i-1][j-1]+comb[i-1][j])%P;}}
    }
     
    int getbitcnt(llong x)
    {
    	return bitcnt[x&524287]+bitcnt[(x>>19)&524287]+bitcnt[(x>>38)&524287];
    }
     
    namespace Solve1
    {
    	void dfs(int pos,llong x)
    	{
    		if(pos==sz) {ans[getbitcnt(x)]++; return;}
    		dfs(pos+1,x); dfs(pos+1,x^a[b[pos]]);
    	}
    }
     
    namespace Solve2
    {
    	llong sta[mxM+3];
    	llong f[mxM+3];
    	void dfs(int pos,int cnt,llong x)
    	{
    		if(pos==m) {f[cnt+getbitcnt(x)]++; return;}
    		dfs(pos+1,cnt,x); dfs(pos+1,cnt+1,x^sta[pos]);
    	}
    	void solve()
    	{
    		for(int i=sz; i<m; i++)
    		{
    			for(int j=0; j<sz; j++)
    			{
    				if(a[b[j]]&(1ll<<b[i])) {sta[i] |= (1ll<<j);}
    			}
    		}
    		dfs(sz,0,0ll);
    //		for(int i=0; i<=m; i++) printf("%I64d ",f[i]); puts("");
    		for(int k=0; k<=m; k++)
    		{
    			for(int i=0; i<=m; i++)
    			{
    				llong coe = 0ll;
    				for(int j=0; j<=i&&j<=k; j++)
    				{
    					llong tmp = comb[i][j]*comb[m-i][k-j]%P; if(j&1) {tmp = P-tmp;}
    					coe+=tmp-P,coe+=(coe>>31)&P;
    				}
    				coe = coe*f[i]%P;
    				ans[k]+=coe-P,ans[k]+=(ans[k]>>31)&P;
    			}
    			ans[k] = ans[k]*quickpow((P+1ll)/2ll,m-sz)%P;
    		}
    	}
    }
     
    int main()
    {
    	initfact(mxM);
    	for(int i=1; i<mxN; i++) bitcnt[i] = bitcnt[i>>1]+(i&1);
    	scanf("%d%d",&n,&m);
    	for(int i=1; i<=n; i++)
    	{
    		llong x; scanf("%I64d",&x);
    		for(int k=m-1; k>=0; k--) if(x&(1ll<<k))
    		{
    			if(!a[k]) {a[k] = x; sz++; break;}
    			else {x ^= a[k];}
    		}
    	}
    	for(int i=m-1; i>=0; i--) if(a[i])
    	{
    		for(int j=i+1; j<m; j++) if(a[j]&(1ll<<i)) {a[j] ^= a[i];}
    	}
    //	for(int i=0; i<m; i++) printf("%I64d ",a[i]); puts("");
    	for(int i=0,j=0; i<sz; i++)
    	{
    		while(!a[j]) {j++;} b[i] = j; j++;
    	}
    	for(int i=sz,j=0; i<m; i++)
    	{
    		while(a[j]) {j++;} b[i] = j; j++;
    	}
    	if(sz<=26)
    	{
    		Solve1::dfs(0,0ll);
    	}
    	else
    	{
    		Solve2::solve();
    	}
    	for(int i=0; i<=m; i++) ans[i] = ans[i]*quickpow(2ll,n-sz)%P;
    	for(int i=0; i<=m; i++) printf("%I64d ",ans[i]); puts("");
    	return 0;
    }
    
  • 相关阅读:
    PHP导入导出Excel方法
    14款优秀的MySQL客户端
    php接收二进制数据流转换成图片
    PHP中curl_setopt的CURLOPT系列 选项(转)
    二十五个顶级PHP模板
    设计模式——观察者模式 Observer
    设计模式——装饰者模式
    关于JS中的constructor与prototype
    解决JQuery和其他库共存
    json 基础知识
  • 原文地址:https://www.cnblogs.com/suncongbo/p/12768044.html
Copyright © 2011-2022 走看看