UOJ370 滑稽树上滑稽果 【状压DP】
题目分析:
答案肯定是链,否则可以把枝干放到主干。
去除一直存在的位,这样0位占满时就会结束。
用$f[S]$表示0位填埋情况,每次转移是它的一个子集,我们考虑可否转移。
再用$g[S]$存储转移是否合法,用滑稽果填充$g$数组。不一定要完全满足条件,因为有其它方案更优,无影响。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 #define RI register 5 6 const int maxn = 202000; 7 8 int n,a[maxn],bit=1,maxx; 9 long long ans = 0; 10 int cnt = (1<<18)-1,res=0; 11 12 int f[1<<18]; 13 int g[1<<18],vol[1<<18]; 14 char buffer[20000000], *buf=buffer; 15 inline void in(int &x) { 16 while(*buf>'9' || *buf<'0') ++buf; 17 for(x=0;*buf>='0'&&*buf<='9'; ++buf) x=x*10+*buf-'0'; 18 } 19 20 inline void read(){ 21 in(n); 22 for(RI int i=1;i<=n;i++) in(a[i]),cnt &= a[i]; 23 for(RI int i=1;i<=n;i++) a[i] -= cnt,maxx=max(maxx,a[i]),res |= a[i]; 24 ans += 1ll*cnt*n; 25 } 26 27 inline void init(){ 28 while((bit<<1)<=maxx)bit<<=1; bit<<=1; res = (bit-1-res); 29 for(RI int i=1;i<=n;i++) vol[bit-1-a[i]]=1; 30 for(RI int i=bit-1;i>=0;i--){ 31 if(!vol[i] || g[i]) continue; 32 for(RI int j=i;j;j=((j-1)&i)){g[j]=1;} 33 } 34 } 35 36 inline void work(){ 37 memset(f,0x3f,sizeof(f)); f[0] = 0; 38 for(RI int now=0;now<bit;now++){ 39 if(f[now] > 1e6) continue; 40 int dt = bit-1-now; 41 for(RI int i=dt;i;i=((i-1)&dt)){ 42 if(g[i]){f[now+i] = min(f[now+i],f[now]+bit-1-(now+i));}; 43 } 44 } 45 ans += f[bit-1]; 46 printf("%lld",ans); 47 } 48 49 int main(){ 50 fread(buffer, 1, (sizeof buffer)-1, stdin); 51 read(); 52 init(); 53 work(); 54 return 0; 55 }