[NOI2008]奥运物流

  • 题目链接:洛谷BZOJ

  • 前置知识:莫得

题解

  • 考虑DP。这题首先发现如果修改后继状态那么一定修改成 \(1\) 。然后现在难点在于如何解决存在一个环的问题以及状态的设计。先考虑是树的情况。

  • 可以发现跟IOI河流Riv一题相似,子树内节点的贡献会被祖先的状态影响。这道题中显然每个节点单独的贡献与其深度有关,那么考虑将其计入状态。现在设 \(f[u][t][d]\) 表示以 \(u\) 为根的子树内一共有 \(t\) 个节点将后继修改为 \(1\) ,且 \(u\) 此时的深度为 \(d\) 的最大贡献。那么我们发现对于一对父子 \(u,v\) ,我们只需特殊处理关于深度为 \(1\) 的边界状态,然后枚举 \(t_u,t_v,d_u\) ,再从 \(d_v=d_u+1\)\(d_v=1\) 的状态背包转移即可。最后注意每个状态要加上 \(C_u\cdot k^d\) ,也就是 \(u\) 自身的贡献。

  • 那么现在考虑环的问题。若设一个节点的深度为 其在不考虑 \(1\) 的后继边所形成的树上的深度 ,若环上一点深度为 \(d\) ,环长为 \(len\) ,那么环带来的影响其实就是是每个节点单独的贡献多了 \(R(1)\cdot k^{len}\) 。那么等比数列求个和我们会发现若在树的情况下我们求出的答案为 \(R'\) ,那么最终 \(R(1)=\frac{R'}{1-k^{len}}\) 。那么我们直接枚举初始强制将哪一个环上节点的后继修改为 \(1\) 得到环长 \(len\) ,然后dp即可。由于题目保证环上节点最多 \(20\) 个,所有最终复杂度为 \(O(20\cdot n^2\cdot m^2)\) ,可以通过此题。

  • 注意修改初始将某一个环上节点的后继为 \(1\) 是强制的,虽然修改后的树中该节点深度为 \(1\) ,但是如果该节点原来的后继不是 \(1\) 的话该节点 \(t =0,d=1\) 的状态不合法,要特判掉。

    #include<bits/stdc++.h>
    #define ll long long
    #define ull unsigned long long
    #define rep(i, s, t) for(int i = s, __ = t; i <= __; ++i)
    #define dwn(i, s, t) for(int i = s, __ = t; i >= __; --i)
    
    const int INF = 2147483647;
    const int MAXN = 100 + 100;
    const int MOD = 998244353;
    using namespace std;
    inline int read(int x = 0, int f = 1){
        char ch = getchar();
        for(; !isdigit(ch); ch = getchar())if(ch == '-')f = -1;
        for(; isdigit(ch); ch = getchar())x = ch - '0' + x * 10;
        return x * f;
    }
    inline void write(int x){
        if(x < 0)x = -x, putchar('-');
        if(x >= 10)write(x / 10); putchar(x % 10 + '0');
        return ;
    }
    
    int n, m, fa[MAXN]; double k, C[MAXN];
    double dp[MAXN][MAXN][MAXN], sup[MAXN][MAXN], pw[MAXN];
    inline void upd(double &x, double y){x = max(x, y); return ;}
    
    void dfs(int u){
    	dp[u][1][1] = C[u] * k;
    	if(dp[u][0][1] != -INF + 1){
    		rep(d, 2, n)dp[u][0][d] = C[u] * pw[d];
    		if(fa[u] == 1)dp[u][0][1] = C[u] * k;
    	}
    	rep(v, 2, n)if(fa[v] == u){
    		dfs(v); rep(i, 0, m)rep(j, 1, n)sup[i][j] = -INF;
    		rep(i, 0, m)rep(j, 0, m){
    			rep(d, 1, n){
    				if(i + j > m)break;
    				upd(sup[i + j][d], dp[u][i][d] + max(dp[v][j][d + 1], dp[v][j][1]));
    			}
    		}
    		rep(i, 0, m)rep(d, 0, n)dp[u][i][d] = sup[i][d];
    	}
    	return ;
    }
    
    double f[MAXN], ans = 0;
    int main(){
    	n = read(), m = read(), scanf("%lf", &k);
    	rep(i, 1, n)fa[i] = read(); rep(i, 1, n)scanf("%lf", &C[i]);
    	pw[0] = 1.0; rep(i, 1, n)pw[i] = pw[i - 1] * k;
    	int cur = fa[1], cnt = 2;
    	while(cur != 1){
    		rep(i, 1, n)rep(j, 0, m)rep(k, 1, n)dp[i][j][k] = -INF;
    		rep(i, 0, m)f[i] = -INF; f[0] = 0; int up = fa[cur]; fa[cur] = 1;
    		int tag = 0; if(up != 1)tag = 1, dp[cur][0][1] = -INF + 1, dfs(cur);
    		rep(v, 2, n)if(fa[v] == 1 && (v != cur || !tag)){
    			dfs(v);
    			dwn(h, m, 0){
    				dwn(i, h, 0){
    					int j = h - i;
    					upd(f[h], f[i] + dp[v][j][1]);
    				}
    			}
    		}
    		if(tag){
    			f[m] = f[m - 1] + dp[cur][1][1];
    			rep(i, 0, m - 2)upd(f[m], f[i] + dp[cur][m - i][1]);
    		}
    		ans = max(ans, (f[m] + C[1]) / (1.0 - pw[cnt]));
    		fa[cur] = up, ++cnt, cur = up;
    	}
    	printf("%.2lf", ans); return 0;
    }
    
posted @ 2019-08-08 15:47  Destinies  阅读(382)  评论(0编辑  收藏  举报