【BZOJ1065】【NOI2008】奥运物流(动态规划)

【BZOJ1065】【NOI2008】奥运物流(动态规划)

题面

BZOJ

洛谷

题解

先不考虑环的情况,于是变成了一棵树。

这样子我们答案的贡献是\(\sum_{i=1}^nC_i\times k^{dep[i]}\)

其中\(dep\)是点的深度

考虑环的影响,显然是\(R(1)\)的贡献沿着环反复加在环上,假设环的长度为\(len\)

那么,答案就是\(R(1)\sum_{i=1}^{\infty}K^{i\times len}\),

等比数列求和一下,所以答案就是

\[\frac{\sum_{i=1}^nC_i\times K^{dep[i]}}{1-K^{len}}
\]

这样子就可以得到\(10\)分啦。


我们考虑一下如果一个点更改后继,我们显然要让他的深度最小

既然后继可以随意更改,那么它显然接到\(1\)的后面。

因此,一个点如果修改后继,那么它的后继一定是\(1\)

同时,因为\(K\lt 1\),所以我们尽可能让环的大小最小。


既然每次修改的结果一定是将后继修改为\(1\)

那么,我们可以考虑一下\(dp\)

设\(f[i][j]\)表示\(i\)的子树中有\(j\)个点的后继修改为\(1\)的子树最大贡献

但是这样子我们似乎没法将\(i\)这个点的贡献转移给\(1\)

因为我们并不知道\(i\)的深度。

所以我们增加一维\(f[i][j][k]\)表示以\(i\)为根的子树中,\(j\)个点的后继修改为\(i\),

其中\(i\)号点到\(1\)的距离是\(k\)的最大贡献

所以我们的转移有两种,一种是将\(i\)号点的后继修改为\(1\)号点

那么,它的所有儿子的深度要么是\(1\),也就是后继修改为了\(1\)

要么是\(2\),也就是没有修改后继

转移就是\(f[u][i][k]=C_u·K+\sum_vmax(f[v][j][1],f[v][j][2])\)

对于第二维是一个背包问题

如果不修改当前点的后继的话,它的所有儿子的深度要么是当前点加一,要么是\(1\)

\(f[u][i][k]=C_u·K^k+\sum_vmax(f[v][j][k+1],f[v][j][1])\)

但是这样子我们发现环还是对答案有影响

所以我们枚举一下环的长度,从对应的地方把它断开成为一棵树,

这样底下的分母的贡献唯一确定,只需要考虑树上的最大贡献了

此时也就是上面所述的\(dp\)了。

说起来好容易,写起来贼麻烦,我自己写WA到不知道哪里去了,最后照着别人打了一遍

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 65
vector<int> e[MAX];
int n,m,fa[MAX],S[MAX],top,deg[MAX];
double K,C[MAX],f[MAX][MAX][MAX],ans,pw[MAX],g[MAX][MAX];
int Cir[MAX];
void cmax(double &x,double y){if(y>x)x=y;}
void init()
{
for(int i=1;i<=n;++i)e[i].clear();
for(int i=1;i<=n;++i)
for(int j=0;j<=n;++j)
for(int k=0;k<=n;++k)f[i][j][k]=-1e18;
}
void predp(int u,int dep)
{
for(int i=0;i<=e[u].size();++i)
for(int j=0;j<=n;++j)g[i][j]=-1e18;
g[0][0]=0;
for(int i=0;i<e[u].size();++i)
{
int v=e[u][i];
for(int k=0;k<=m;++k)
for(int l=0;l<=k;++l)
cmax(g[i+1][k],g[i][l]+f[v][k-l][dep]);
}
}
void dfs(int u)
{
for(int i=0;i<e[u].size();++i)dfs(e[u][i]);
predp(u,2);
for(int i=0;i<=n;++i)
for(int j=1;j<=m;++j)
f[u][j][i]=C[u]*K+g[e[u].size()][j-1];
for(int i=0;i<=n;++i)
{
predp(u,i+1);
for(int j=0;j<=m;++j)
cmax(f[u][j][i],g[e[u].size()][j]+C[u]*pw[i]);
}
}
int main()
{
scanf("%d%d%lf",&n,&m,&K);pw[0]=1;
for(int i=1;i<=n;++i)pw[i]=pw[i-1]*K;
for(int i=1;i<=n;++i)scanf("%d",&fa[i]);
for(int i=1;i<=n;++i)scanf("%lf",&C[i]);
for(int i=fa[1],len=2;i!=1;i=fa[i],++len)
{
init();
for(int j=n;j>1;--j)
if(i!=j)e[fa[j]].push_back(j);
else e[1].push_back(j);
dfs(1);
ans=max(ans,f[1][m-(fa[i]!=1)][0]/(1-pw[len]));
}
printf("%.2lf\n",ans);
return 0;
}
上一篇:一分钟掌握Spring中bean的生命周期!


下一篇:Unity Adam特性整理