1 2 3 4

msc的宠物 二分+树形DP

https://ac.nowcoder.com/acm/contest/7016/E

这题真没想到

考虑一个问题,删除边对极差的影响,-------无论如何删除边都有办法不让让答案变大(可能相等)

利用二分答案,每次算出mid都跑一次dp看看能不能k条边以内完成,dp[x][i]表示以x为根的子树,所有点的数值在list[i] -- list[i] + mid之间最少删除dp[x][i]个边

ans[x]表示ans[x]极差在mid之间最少删除ans[x] t条边

 

具体在代码

 

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 2000+11;
typedef long long ll;
ll inf = 1e15;
ll mid;
ll dp[maxn][maxn];
ll list[maxn];

vector<int>G[maxn];
void add(int x,int y){
    G[x].push_back(y);
}
int n,k;
ll ans[maxn];

int dfs(int x,int fa){ 
	//cout<<x<<endl;
    for(int i = 1;i<=n;i++){
        if(list[x] >= list[i] && list[i] + mid >= list[x]){
            dp[x][i] = 0;
        }
        else{
            dp[x][i] = inf;
        }
    }
    
    for(int i=0;i<G[x].size();i++){
        int p = G[x][i];
        if(p == fa) continue;
        dfs(p,x);

        for(int j=1;j<=n;j++){
            dp[x][j] += min(dp[p][j],ans[p] + 1);
        }
    }
    for(int i = 1;i<=n;i++){
       ans[x] = min(ans[x],dp[x][i]);
    }
    return 0;
}

int jude(){
    for(int i=0;i<=n;i++){
        ans[i] = inf;
    }
    dfs(1,-1);
    return (ans[1] <= k);//可行 
}
int main(){
    
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
    	ans[i] = inf;
        scanf("%lld",&list[i]);
    }
    
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
  	
    ll l = 0;
    ll r = 2e9+7;
    ll cns = 0;
    
    for(int i=0;i<111;i++){
        mid = (l+r)/2;
        if(jude()){//可以,区间加大
            cns = mid;
			r = mid - 1;
        }
        else{
            l = mid + 1;
        }
    }
    
    printf("%lld\n",cns);
    return 0;
}

  

posted @ 2020-08-20 00:47  Lesning  阅读(114)  评论(0编辑  收藏  举报