BZOJ4762 最小集合

来源:互联网 发布:淘宝直通车删除计划 编辑:程序博客网 时间:2024/06/11 10:35

先考虑把所有数取反,问题变成求使得所有数的或和为全集,且任意一个缺一个数的子集的或和都不为全集的方案数

考虑dp,f[i][j][k]表示在前i个数中选了一些,这些数或和为j,要求k是后n-i个数里选出来的数的或和的子集的方案数

不要问我为什么会有这种鬼畜状态-_-

那么考虑转移,设第i+1个数为x,假设第i+1个数不选,那么f[i+1][j][k]+=f[i][j][k]

如果第i个数选,那么我们先保证x不是前面选的数的子集,即以下转移要求x&j!=x

经WerKeyTom_FTD神犇提醒,观察下面的转移式,我们发现当x&j==x的时候,相当于没做转移,所以不用判这个

那么考虑用所有的方案减去x是后面选的数的子集的方案,即

f[i+1][j|x][k^(k&x)]+=f[i][j][k]//所有的的方案

f[i+1][j|x][(k^(k&x))|(x^(x&j))]-=f[i][j][k]//不合法的方案

那么这样复杂度就是n*4^m的,m为二进制位的个数

观察转移式中k那一维的表达式,初始值为f[0][0][0]=1,那么瞎jb归纳以下可得f[i][j][k]!=0的必要条件是k是j的子集

那么复杂度就降到n*3^m了

#include<iostream>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<cstdlib>#include<cstdio>#include<map>#include<bitset>#include<set>#include<stack>#include<vector>#include<queue>using namespace std;#define MAXN 1024#define MAXM 1010#define ll long long#define eps 1e-8#define MOD 1000000007#define INF 1000000000int N=1023;int n;int f[MAXN][MAXN],g[MAXN][MAXN];int main(){int i,j,k,x;scanf("%d",&n);f[0][0]=1;for(i=1;i<=n;i++){scanf("%d",&x);x^=N;for(j=0;j<=N;j++){for(k=j;;k=k-1&j){g[j][k]=0;if(!k){break;}}}for(j=0;j<=N;j++){if((x&j)!=x){for(k=j;;k=k-1&j){(g[j|x][k&(~x)]+=f[j][k])%=MOD;(g[j|x][(k&(~x))|(x&(~j))]+=MOD-f[j][k])%=MOD;if(!k){break;}}}}for(j=0;j<=N;j++){for(k=j;;k=k-1&j){(f[j][k]+=g[j][k])%=MOD;if(!k){break;}}}}printf("%d\n",f[N][0]);return 0;}/*41 2 4 4*/


0 0
原创粉丝点击