• CF1605F PalindORme 解题报告


    CF1605F PalindORme 解题报告:

    更好的阅读体验

    题意

    定义一个序列是好的当且仅当任意前缀的按位与都等于与其等长的后缀的按位与,定义一个序列是合法的当且仅当其重排之后是好的,求长度为 \(n\),值域为 \([0,2^k-1]\) 的合法序列数量。

    \(1\leqslant n,k\leqslant 80\)

    分析

    Div.2 无人场切的题目竟然只有 *2900/jy。

    用一种更加方便的方式刻画合法序列,我们发现每一个合法序列都能通过以下操作删至不超过一个数,同样一个不合法序列一定不行。

    1. 令变量 \(V=0\)
    2. 从序列中选出两个数 \(x,y\) 使得 \(x\text{ or }V=y\text{ or }V\)
    3. \(V\leftarrow x\text{ or }V\),删除 \(x,y\) 然后回到第二步。

    一个合法序列一定不会被判为不合法序列是因为你每一步没有选择的合法 \((x,y)\) 在之后的步骤一定能继续用。

    我们发现利用这个性质仍然不好对合法串计数,但是可以尝试通过这一操作搭建合法序列与不合法序列之间的桥梁,然后容斥算出其对应合法序列数量。

    定义一个不合法序列的最优子序列为其经过操作后移除的序列(若存在一个和 \(V\) 的按位与等于自己的数,就将其放在最优子序列的中间)

    可以发现一个不合法序列剩下的数都满足,去掉 \(V\)\(1\) 的二进制位,任意数不为 \(0\) 且两两不同,于是我们建立了一个不合法序列与合法序列(序列内部无序)之间的映射,其中合法序列长度、二进制位数量均小于不合法序列。

    接下来就是正常流程了,令 \(f_{i,j}\) 为长度为 \(i\),二进制位数量恰好为 \(j\) 的序列数量,\(g_{i,j}\) 为长度为 \(i\),二进制位数量恰好为 \(j\) 的不合法序列数量。

    \(f_{i,j}\) 明显可以容斥预处理,也就是:

    \[f_{i,j}=\sum_{k=0}^j (-1)^{j-k}{j\choose k}(2^k)^i \]

    \(g_{i,j}\) 可以直接枚举其最优子序列的长度与二进制位个数,然后有:

    \[g_{i,j}=\sum_{i'=0}^{i-1}\sum_{j'=0}^{j-1}{i\choose i'}{j\choose j'}(2^{j'})^{i-i'}h_{i-i',j-j'}(f_{i',j'}-g_{i',j'}) \]

    前面两个组合数的系数很显然,二的幂次的系数是因为被删去的二进制位可以任意放,\(h_{i,j}\) 是长度为 \(i\),二进制位数量恰好为 \(j\),数字非零且两两不同的方案数,直接容斥计算即可:

    \[h_{i,j}=\sum_{k=0}^j (-1)^{j-k}{j\choose k}(2^k-1)^{\underline i} \]

    但是还有一点问题,一个长度为奇数的不合法序列不能从它长度减一的合法序列转移过来,因为显然这也是合法序列。

    然后就做完了,复杂度 \(O(n^2k^2)\)

    代码

    #include<stdio.h>
    const int maxn=105;
    int n,m,mod,ans;
    int C[maxn][maxn],f[maxn][maxn],g[maxn][maxn],h[maxn][maxn],mul2[maxn*maxn],mul[2];
    int calc(int n,int m){
    	int res=1;
    	for(int i=n;i>=n-m+1;i--)
    		res=1ll*res*i%mod;
    	return res;
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&mod),mul[0]=1,mul[1]=mod-1;
    	for(int i=0;i<=100;i++){
    		C[i][0]=C[i][i]=1;
    		for(int j=1;j<i;j++)
    			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    	}
    	mul2[0]=1;
    	for(int i=1;i<=n*m;i++)
    		mul2[i]=(mul2[i-1]+mul2[i-1])%mod;
    	for(int i=0;i<=n;i++)
    		for(int j=0;j<=m;j++)
    			for(int k=0;k<=j;k++){
    				f[i][j]=(f[i][j]+1ll*C[j][k]*mul[(j-k)&1]%mod*mul2[i*k])%mod;
    				h[i][j]=(h[i][j]+1ll*C[j][k]*mul[(j-k)&1]%mod*calc(mul2[k]-1,i))%mod;
    			}
    	for(int i=0;i<=n;i++)
    		for(int j=0;j<=m;j++)
    			for(int x=0;x<i;x++)
    				for(int y=0;y<j;y++){
    					if((n&1)&&(i==n)&&(x==n-1))
    						continue;
    					g[i][j]=(g[i][j]+1ll*C[i][x]*C[j][y]%mod*mul2[(i-x)*y]%mod*h[i-x][j-y]%mod*(f[x][y]-g[x][y]+mod))%mod;
    				}
    	for(int i=0;i<=m;i++)
    		ans=(ans+1ll*C[m][i]*(f[n][i]-g[n][i]+mod))%mod;
    	printf("%d\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    Java实时读取日志文件
    Trie树的应用:查询IP地址的ISP
    vue.extend,mixins和vue.component的区别
    前端性能优化10个方面
    vue组件和插件是实现
    vue指令用法
    promise retry实现
    linux管道与重定向
    linux文件颜色与类型
    linux文件权限说明
  • 原文地址:https://www.cnblogs.com/xiaoziyao/p/15801470.html
Copyright © 2020-2023  润新知