题目
2008 北京奥运会即将开幕,举国上下都在为这一盛事做好准备。为了高效率、 成功地举办奥运会,对物流系统进行规划是必不可少的。
物流系统由若干物流基站组成,以 1…N 进行编号。每个物流基站 i 都有且 仅有一个后继基站 Si,而可以有多个前驱基站。基站 i 中需要继续运输的物资都 将被运往后继基站 Si,显然一个物流基站的后继基站不能是其本身。编号为 1 的 物流基站称为控制基站,从任何物流基站都可将物资运往控制基站。注意 控制基 站也有后继基站,以便在需要时进行物资的流通。在物流系统中,高可靠性与低 成本是主要设计目。对于基站 i,我们定义其“可靠性” () R i 如下:
设物流基站 i 有 w 个前驱基站 P1,P2,…,Pw ,即这些基站以 i 为后继基站,则基 站 i 的可靠性 R(i)满足下式:
R(i)=Ci+kj=1∑wR(Pj)
其中 Ci和 k 都是常实数且恒为正,且有 k 小于 1。
整个系统的可靠性与控制基站的可靠性正相关,我们的目标是通过修改物流 系统,即更改某些基站的后继基站,使得控制基站的可靠性 R(1)尽量大。但由于 经费限制,最多只能修改 m 个基站的后继基站,并且,控制基站的后继基站不 可被修改。因而我们所面临的问题就是,如何修改不超过 m 个基站的后继,使 得控制基站的可靠性 R(1)最大化。
输入描述
第一行包含两个整数与一个实数,N, m, k。其中 N 表示基 站数目,m 表示最多可修改的后继基站数目,k 分别为可靠性定义中的常数。
第二行包含 N 个整数,分别是 S1, S2…SN,即每一个基站的后继基站编号。
第三行包含 N 个正实数,分别是 C1, C2…CN,为可靠性定义中的常数。
输出描述
仅包含一个实数,为可得到的最大 R(1)。精确到小数点两位。
样例输入
4 1 0.5 2 3 1 3 10.0 10.0 10.0 10.0
样例输出
30.00
讲解
-
考虑DP。这题首先发现如果修改后继状态那么一定修改成 11 。然后现在难点在于如何解决存在一个环的问题以及状态的设计。先考虑是树的情况。
-
可以发现跟IOI河流Riv一题相似,子树内节点的贡献会被祖先的状态影响。这道题中显然每个节点单独的贡献与其深度有关,那么考虑将其计入状态。现在设 f[u][t][d]f[u][t][d] 表示以 uu 为根的子树内一共有 tt 个节点将后继修改为 11 ,且 uu 此时的深度为 dd 的最大贡献。那么我们发现对于一对父子 u,vu,v ,我们只需特殊处理关于深度为 11 的边界状态,然后枚举 tu,tv,dutu,tv,du ,再从 dv=du+1dv=du+1 或 dv=1dv=1 的状态背包转移即可。最后注意每个状态要加上 Cu⋅kdCu⋅kd ,也就是 uu 自身的贡献。
-
那么现在考虑环的问题。若设一个节点的深度为 其在不考虑 11 的后继边所形成的树上的深度 ,若环上一点深度为 dd ,环长为 lenlen ,那么环带来的影响其实就是是每个节点单独的贡献多了 R(1)⋅klenR(1)⋅klen 。那么等比数列求个和我们会发现若在树的情况下我们求出的答案为 R′R′ ,那么最终 R(1)=R′1−klenR(1)=R′1−klen 。那么我们直接枚举初始强制将哪一个环上节点的后继修改为 11 得到环长 lenlen ,然后dp即可。由于题目保证环上节点最多 2020 个,所有最终复杂度为 O(20⋅n2⋅m2)O(20⋅n2⋅m2) ,可以通过此题。
-
注意修改初始将某一个环上节点的后继为 11 是强制的,虽然修改后的树中该节点深度为 11 ,但是如果该节点原来的后继不是 11 的话该节点 t=0,d=1t=0,d=1 的状态不合法,要特判掉。
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define space putchar(' ')
#define enter putchar('\n')
#define eps 1e-10
#define ba 47
#define MAXN 5005
//#define ivorysi
using namespace std;
typedef long long int64;
typedef unsigned int u32;
typedef double db;
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 +c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
int N,M;
int fa[65];
db K[65],C[65],f[65][65][65],g[65];
bool nouse[65];
void dfs(int u,int dep) {
for(int v = 2 ; v <= N ; ++v) {
if(fa[v] == u) dfs(v,dep + 1);
}
for(int d = dep ; d >= min(dep,2) ; --d) {
memset(g,0,sizeof(g));
for(int v = 1 ; v <= N ; ++v) {
if(fa[v] == u) {
for(int j = M ; j >= 0 ; --j) {
for(int k = j ; k >= 0 ; --k) {
g[j] = max(g[j],g[k] + max(f[v][j - k][d + 1],f[v][j - k][1]));
}
}
}
}
for(int j = 0 ; j <= M ; ++j) f[u][j][d] = g[j] + C[u] * K[d];
}
if(!nouse[u] && fa[u] != 1) {
memset(g,0,sizeof(g));
for(int v = 1 ; v <= N ; ++v) {
if(fa[v] == u) {
for(int j = M ; j >= 0 ; --j) {
for(int k = j; k >= 0 ; --k) {
g[j] = max(g[j],g[k] + max(f[v][j - k][2],f[v][j - k][1]));
}
}
}
}
for(int j = 0 ; j < M ; ++j) f[u][j + 1][1] = g[j] + C[u] * K[1];
}
}
void Init() {
read(N);read(M);scanf("%lf",&K[1]);
for(int i = 1 ; i <= N ; ++i) {
read(fa[i]);
}
K[0] = 1;
for(int i = 2 ; i <= N ; ++i) {
K[i] = K[i - 1] * K[1];
}
for(int i = 1 ; i <= N ; ++i) {
scanf("%lf",&C[i]);
}
}
void Solve() {
int p = 1;
db ans = 0;
for(int len = 2 ; len ; ++len) {
p = fa[p];
if(p == 1) break;
memset(nouse,0,sizeof(nouse));
int t = 1,l = 0;
while(l != len) {
nouse[t] = 1;
t = fa[t];
++l;
}
int rec = 0;
if(fa[p] != 1) {--M;rec = fa[p];fa[p] = 1;}
memset(f,0,sizeof(f));
dfs(1,0);
db res = f[1][M][0] / (1 - K[len]);
ans = max(ans,res);
if(rec) {++M;fa[p] = rec;}
}
printf("%.2lf\n",ans);
}
int main(){
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
}
2138

被折叠的 条评论
为什么被折叠?



