bzoj1812: [Ioi2005]riv
太假了居然过了样例就A了
这个做法应该是一个O(n^4)的,相信大家都看出来了
这是一个树背包的问题,相信大家也都看出来了
然而怎么背包是个问题QWQ,因为选择了一个点建伐木场,就会影响父亲节点的决策
我只会O(n^2kmaxW)(相当于O(n^5))假设到了当前点有多少木头没有被运到伐木场......
%了一发,发现一个很流弊的操作
f[i][j][u]表示以i为根的子树,修了j个伐木场,最远的伐木场在u的最小花费
规定了最远的u,那就可以直接算当前子树的贡献了
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cmath> using namespace std; struct node { int x,y,next; }a[110];int len,last[110]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int n,m,w[110],fa[110],dep[110],tot[110]; int f[110][110][110];//以i为根的子树,修了j个伐木场,最远的伐木场在u的最小花费 int tim[110],ti[110][110][110]; void treeDP(int x) { f[x][1][x]=0; if(x==0)f[x][0][x]=0; for(int u=fa[x];u!=-1;u=fa[u]) f[x][0][u]=(dep[x]-dep[u])*w[x]; tot[x]=1; for(int k=last[x];k;k=a[k].next) { int y=a[k].y;dep[y]+=dep[x]; treeDP(y); tim[x]++; for(int i=min(tot[x],m);i>=0;i--) for(int j=min(tot[y],m);j>=0;j--) { if(i+j>m)continue; for(int u=x;u!=-1;u=fa[u]) if(f[x][i][u]!=f[n+1][m+1][n+1]) { if(ti[x][i+j][u]!=tim[x]) { ti[x][i+j][u]=tim[x]; f[x][i+j][u]=f[x][i][u]+min(f[y][j][u],f[y][j][y]); } else f[x][i+j][u]=min(f[x][i+j][u],f[x][i][u]+min(f[y][j][u],f[y][j][y])); } } tot[x]+=tot[y]; } } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d%d",&n,&m); len=0;memset(last,0,sizeof(last)); for(int i=1;i<=n;i++) { scanf("%d%d%d",&w[i],&fa[i],&dep[i]); ins(fa[i],i); } memset(f,63,sizeof(f)); memset(tim,0,sizeof(tim)); memset(ti,0,sizeof(ti)); fa[0]=-1;dep[0]=0;treeDP(0); printf("%d\n",f[0][m][0]); return 0; }
pain and happy in the cruel world.