记录编号 184588 评测结果 AAAAAAAAAA
题目名称 [NOI 2008]奥运物流 最终得分 100
用户昵称 Gravatarmikumikumi 是否通过 通过
代码语言 C++ 运行时间 0.668 s
提交时间 2015-09-03 17:10:25 内存使用 3.40 MiB
显示代码纯文本
#include<cstdio>
#include<deque>
#include<cstring>
#include<iostream>
using namespace std;
double f[61][61][61]={0},g[61][61][61]={0};
int N,m,father[80];
double powk[80]={0};
double k,C[65];
deque<int> s[70];
void DP(int now,int x,int dep)//now是当前将哪个点从环上切断了,dep是当前节点的深度
{
	for(int i=0;i<s[x].size();i++) if(s[x][i]!=now&&s[x][i]!=1) DP(now,s[x][i],dep+1);
	for(int i=1;i<=dep;i++)//枚举x到1的距离
	{
		int tem=0;
		if(dep>1&&i==1)//对x进行了修改
			tem=1;
		double F[70]={0};
		for(int q=0;q<s[x].size();q++)
		{
			int v=s[x][q];
			if(v==1||v==now) continue;
			for(int j=m;j>=0;j--)
				for(int p=j;p>=0;p--)//分组背包
				{
					F[j]=max(F[j],F[p]+g[v][j-p][i]);
				}
	    }
		for(int j=tem;j<=m;j++) f[x][j][i]=F[j-tem]+C[x]*powk[i];
	}
	for(int i=0;i<=m;i++)
		for(int d=0;d<dep;d++) g[x][i][d]=max(f[x][i][d+1],f[x][i][1]);
}
void work()
{
	int len=2;//环的长度
	double ans=0;
	for(int i=father[1];i!=1;i=father[i],len++)//枚举将环上的哪个点连到1上
	{
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		for(int j=2;j<=N;j++) if(father[j]==1||j==i) DP(i,j,1);//对1的子树进行DP
		double F[70]={0};
		for(int x=2;x<=N;x++)//对1的子树进行泛化背包
		{
			if(!(father[x]==1||x==i)) continue;
			for(int j=m;j>=0;j--)
				for(int k=j;k>=0;k--)
				{
					F[j]=max(F[j],F[k]+f[x][j-k][1]);
				}
		}
		int tem;
		if(father[i]==1) tem=m;//当i的后继是1是,不用修改后继
		else tem=m-1;
		for(int j=0;j<=tem;j++) ans=max(ans,(F[j]+C[1])/(1-powk[len]));
	}
	printf("%.2lf",ans);
}
int main()
{
	freopen("trans.in","r",stdin);
	freopen("trans.out","w",stdout);
	scanf("%d%d%lf",&N,&m,&k);
	for(int i=1;i<=N;i++)
	{
		scanf("%d",&father[i]);
		s[father[i]].push_back(i);
	}
	for(int i=1;i<=N;i++) scanf("%lf",&C[i]);
	powk[0]=1;
	for(int i=1;i<=N;i++) powk[i]=powk[i-1]*k;
	work();
	return 0;
}