[NOI2008]奥运物流

题解

  • 考虑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;
    }

转载于:https://www.cnblogs.com/gdc-destinies/p/11321604.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值