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 }

猜你喜欢

转载自www.cnblogs.com/Menhera/p/9277639.html