Wannafly挑战赛28 msc的宠物

题意:

给定一棵树,树上每个节点有权值,要求删除最多k条边使得所有联通块的最大值和最小的差最大的最小

分析:

最小化最大值,很明显是二分最大的差值D了
转化一下题目其实就是将树上的节点分成k个联通块,使得所有联通块的最大值和最小的差最大的最小。

状态:

  • f[x] 表示至少删除F[x] 个才能使得x子树差值不超过D
  • g[x][u] 表示至少删除g[x][u] 个才能使得子树最大值为a[u]

状态转移:

设y是x的子节点

  • 如果 a[y] <= a[u] <= a[y]+D,g[x][u] = min(f[y]+1,g[x][u]);(直接断开这条边或者留下这条边)
  • 否则g[x][u] = f[y]+1; (直接断开这条边)
  • f[x] = min(g[x][u]);

参考代码


#include <bits/stdc++.h>
#define Pb push_back
using namespace std;
typedef long long LL;
// const int INF = 0x7FFFFFFF;
const LL     INFF =1e15;
const int maxn = 1000+10;
LL f[maxn],g[maxn][maxn];
LL a[maxn];
LL val[maxn];

std::vector<int> G[maxn];
LL D;
 int n,k;
void dfs(int node,int fa){
  for(int i = 1;i <= n; ++i)
    {
      if(a[node] <= a[i] && a[i] <= a[node]+ D)
         g[node][i] = 0;
      else
         g[node][i] = INFF;
    }

  for(int i = 0;i < (int) G[node].size();++i){
    int v = G[node][i];
    if(v == fa) continue;
    dfs(v,node);
    for(int u = 1;u <= n; ++u){
       if(a[v] <= a[u] && a[u] <= a[v]+D)
        g[node][u]  += min(f[v]+1,g[v][u]);
       else
        g[node][u]  += f[v]+1;
    }
  }
  f[node] = INFF;
  for(int i = 1;i <= n; ++i)
    f[node] = min(f[node],g[node][i]);
}
LL check(LL mid){

  D = mid;
  dfs(1,-1);
  // cout<<D<<" "<<f[1]<<endl;
  // cout<<f[1]<<endl;
  return f[1];
}
int main(void)
{
  
   cin>>n>>k;
   for(int i = 1;i <= n; ++i)
      scanf("%lld",&a[i]);//,val[i] = a[i];
   // sort(val+1,val+n+1);
   for(int i = 1,v,u;i < n; ++i){
     scanf("%d%d",&u,&v);
     G[u].Pb(v);
     G[v].Pb(u);
   }
   LL l = 0,r = 1000000000+2;
   while(r >= l){
       LL mid = (r+l)>>1;
       if(check(mid) <= k)// < 说明差值可以缩小,> 说明差值过小
          r = mid-1;
       else
          l = mid+1;
   }
   cout<<l<<endl;


   return 0;
}