【Luogu】P2465山贼集团(树形状压DP)
写了个70分暴力还挂了,第一遍提交只拿了十分……海星
首先建虚拟节点多叉树转成二叉,然后子集枚举DP
设g[x][i]是以x为根的子树内山贼集合i,x啥都不选也没贡献的时候的最大价值
f[x][i]是要求的答案
然后状压DP即可。
#include<cstdio> #include<algorithm> #include<cctype> #include<cstring> #include<cstdlib> #define maxn 200 #define maxp 13 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } struct Edge{ int next,to; }; struct Pic{ Edge edge[maxn*200]; int head[maxn*100],num; Pic(){num=0;} void add(int from,int to){ edge[++num]=(Edge){head[from],to}; head[from]=num; } }Old,New; int cst[maxn][maxn]; int tot; int mon[1<<maxp]; int dl[maxn],ndl[maxn*100]; int f[maxn*3][1<<maxp]; int g[maxn*3][1<<maxp]; int cost[maxn][1<<maxp]; int vl[1<<maxp]; int n,p; void chan(int x,int fa){ int now=x; for(int i=Old.head[x];i;i=Old.edge[i].next){ int to=Old.edge[i].to; if(to==fa) continue; if(ndl[now]==0||(ndl[now]==1&&dl[x]==1)){ New.add(now,to); ndl[now]++; dl[x]--; } else{ New.add(now,++tot); now=tot; New.add(now,to); ndl[now]++; dl[x]--; } chan(to,x); } } void dfs(int x,int fa){ int lson=0,rson=0; f[x][0]=0; g[x][0]=0; for(int i=New.head[x];i;i=New.edge[i].next){ int to=New.edge[i].to; if(to==fa) continue; if(lson==0) lson=to; else rson=to; dfs(to,x); } if(x>n){ if(rson==0){ for(int i=1;i<(1<<p);++i) f[x][i]=g[x][i]=f[lson][i]; return; } for(int i=1;i<(1<<p);++i){ for(int j=i;j;j=(j-1)&i) g[x][i]=max(g[x][i],f[lson][j]+f[rson][i^j]); g[x][i]=max(g[x][i],f[lson][0]+f[rson][i]); f[x][i]=g[x][i]; } } else{ if(rson==0){ for(int i=1;i<(1<<p);++i){ g[x][i]=f[lson][i]; f[x][i]=g[x][i]+mon[i]; } for(int i=1;i<(1<<p);++i) for(int j=i;j;j=(j-1)&i) f[x][i]=max(f[x][i],g[x][i^j]-cost[x][j]+mon[i]); return; } for(int i=1;i<(1<<p);++i){ for(int j=i;j;j=(j-1)&i) g[x][i]=max(g[x][i],f[lson][j]+f[rson][i^j]); g[x][i]=max(g[x][i],f[lson][0]+f[rson][i]); f[x][i]=g[x][i]+mon[i]; } for(int i=1;i<(1<<p);++i) for(int j=i;j;j=(j-1)&i){ int costx=cost[x][j],state=i^j; f[x][i]=max(f[x][i],g[x][state]+mon[i]-costx); } } } int main(){ memset(f,-127/3,sizeof(f)); memset(g,-127/3,sizeof(g)); g[0][0]=0; f[0][0]=0; n=read(),p=read(); tot=n; for(int i=1;i<n;++i){ int from=read(),to=read(); Old.add(from,to); Old.add(to,from); dl[from]++; dl[to]++; } for(int i=2;i<=n;++i) dl[i]--; for(int i=1;i<=n;++i) for(int j=1;j<=p;++j) cst[i][j]=read(); int T=read(); while(T--){ int val=read(),cnt=read(),state=0; for(int i=1;i<=cnt;++i){ int x=read(); state|=(1<<(x-1)); } vl[state]+=val; } for(int i=1;i<(1<<p);++i) for(int j=i;j;j=(j-1)&i) mon[i]+=vl[j]; for(int i=1;i<=n;++i) for(int j=1;j<(1<<p);++j){ int costx=0; for(int k=1;k<=p;++k) if(j&(1<<(k-1))) costx+=cst[i][k]; cost[i][j]=costx; } chan(1,1); dfs(1,1); printf("%d\n",f[1][(1<<p)-1]); } /* 9 3 1 2 1 3 1 4 2 5 3 6 3 7 3 8 6 9 */