bzoj1065【Noi2008】奥运物流
题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1065
给一棵基环树,每个点i的权值=ci+k*∑son[i],修改至多m个点的父亲使1号点权值最大
sol: 首先因为转移的式子形成了一个环,所以1号点的权值需要手推QAQ
对于一个点,其贡献为ci*k^dep*(1+k^len+k^2len+...),因为每个点更新到1后还要在环上反复更新
所以R1=(∑(ci*k^dep[i]),(i from 1 to n))/(1-k^len)
还是没法做啊QAQ,必须要拆环
枚举环长为len,将1的后继设为断点,就可以只考虑分子啦QwQ,然后对于每个环长做dp取max即可
对于每次修改,易证将其后继设为1最优
考虑dp,f[i][j][k]表示以i为根的子树修改了j次,i到1的距离为k,即可得到状态转移方程
f[i][j][dep]=max {∑ max(f[v][J][dep+1],f[v][J][1])+(c[i]*(k^dep)),v为i儿子,∑J=j;//i不向根连边
∑ max(f[v][J][2],f[v][J][1])+(c[i]*k),v为i儿子,∑J=j; }//i向根连边
P.S.注意特判i=1的情况
然而这样复杂度依然爆炸QAQ
观察右面的式子,首先先简化方程,令g[i][j][k]=max(f[i][j][k+1],f[i][j][1])
则右面的式子可以理解为,对于j,选择一种划分方案,使∑ g[i][J][k],(∑J=j) 最大
即可用多重背包优化该方程,把g看做物品,cost=J,val=g[i][J][k]
P.S.并不是很明白为什么用邻接表遍历就GG了QAQ,直接暴力扫&判定才过QAQ
#include<iostream> #include<algorithm> #include<cstdio> #include <cstring> using namespace std; const int Mx = 80; double f[Mx][Mx][Mx],g[Mx][Mx][Mx]; double C[Mx],F[Mx],K[Mx],ans; int n,m,fa[Mx]; void Dp(int x,int dep) { for(int v=2;v<=n;v++) if(fa[v]==x) Dp(v,dep+1); for(int d=min(2,dep);d<=dep;d++)//不修改后继 { memset(F,0,sizeof(F)); for(int v=2;v<=n;v++)//多重背包优化 if(fa[v]==x) for(int j=m;j>=0;j--) for (int k=j;k>=0;k--) F[j]=max(F[j],F[k]+g[v][j-k][d]); for(int j=0;j<=m;j++) f[x][j][d]=F[j]+C[x]*K[d]; } if(dep>1)//将其后继结点修改为1 { memset(F,0,sizeof(F)); for(int v=2;v<=n;v++) if(fa[v]==x) for(int j=m;j>=0;j--) for(int k=j;k>=0;k--) F[j]=max(F[j],F[k]+g[v][j-k][1]); for(int j=1;j<=m;j++) f[x][j][1]=F[j-1]+C[x]*K[1]; } for(int j=0;j<=m;j++) for(int d=0;d<dep;d++) g[x][j][d]=max(f[x][j][d+1],f[x][j][1]); } int main() { cin>>n>>m>>K[1]; K[0]=1; for (int i=2;i<=n;i++) K[i]=K[i-1]*K[1]; for(int i=1;i<=n;i++) { int x;scanf("%d",&x); fa[i]=x; } for(int i=1;i<=n;i++) cin>>C[i]; for(int now=fa[1],len=2;now!=1;now=fa[now],len++)//枚举环长 { memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); int tmp=fa[now]; double sum=0; fa[now]=1;//断环 for(int v=2;v<=n;v++) if(fa[v]==1) Dp(v,1); memset(F,0,sizeof(F)); for(int v=2;v<=n;v++)//因为1的儿子未修改,所以用f而不是g更新F if(fa[v]==1) for(int j=m;j>=0;j--) for(int k=j;k>=0;k--) F[j]=max(F[j],F[k]+f[v][j-k][1]); for(int j=0;j<m;j++) sum=max(sum,F[j]); if(tmp==1) sum=max(sum,F[m]);//若now的fa为1,则断环未修改 ans=max(ans,(sum+C[1])/(1-K[len]));//更新答案 fa[now]=tmp;//还原父结点 } printf("%.2lf\n",ans); return 0; }