• 「解题报告」[luoguP6594]换寝室 (二分答案 树形DP)


    传送门

    题面

    题意

    (n) 个寝室, 构成一个树结构.

    每个寝室有一个点权 (h_i).

    树上的边可以被禁用.

    一个连通块的不满意值为这个连通块中寝室权值中最大值与最小值的差.

    (m) 个老师, 每个老师有一条查寝路线. 一个老师的不满意值为他的查寝路线中被禁用的边的数量.

    求出在满足所有老师的不满意值之和小于等于 (k) 的情况下, 所有连通块的不满意值 的最大值最小.

    数据范围

    (1 le n le 800, 1 le m le 10^5, 1 le h_i le 10^9, 0 le k le 8 imes 10^7.)


    思路

    一道简单题做了一个上午... 脑子可能是被门夹了...

    最大值最小, 明显的二分答案.

    老师的总不满意度要 (le k), 考虑把不满意度转化为边的权值. 若一条边被 (x) 条查寝路线经过, 则权值为 (x), 用树上差分弄一下就行了.

    这样的话, 我们的任务就转化为 : 在树上删去若干条边, 满足每个联通块内点的权值之差最大值 (le lim), 并使删去边权之和最小.

    (DP)

    考虑树型 (DP) . 先对点的权值进行离散化. 设 (f[u][l][r]) 为 : 考虑点 (u) 的子树, (u) 所在的联通块的点权在 ([l,r]) 范围内时, 删去边权之和的最小值.

    (minx[u]=min{ f[u][l][r] mid 1le l le r le max\_val}) ,

    (v)(u) 的子节点, (wgt[u])(u) 的父边边权.

    若 $val[u]<l $ 或 $ val[u]>r$, 则 (f[u][l][r]=inf),

    否则 (f[u][l][r]=sum min(f[v][l][r],minx[v]+wgt[v])).

    复杂度为 (O(n^3log val)), 考虑优化.

    优化

    发现当 (l) 确定时, (r) 的范围可以根据 (l+lim) 确定, 且当 (r < l+lim) 的时候, 答案一定没有 (r=l+lim) 的时候优秀, 所以可以直接钦定 (r=l+lim).

    所以, 可以删去 (r) 这一维, 将状态改变为 (f[u][l]), 转移类似.

    并且, 由于我们不需要比较点权大小, 所以实际上不需要离散化. 把 (f[u][i]) 的意义改为 (l=val[i]) 时删去边权之和的最小值就行了.


    代码

    #include<bits/stdc++.h>
    #define pb push_back
    #define sz(x) (int)(x).size()
    using namespace std;
    const int _=800+7;
    const int L=20;
    
    int n,m,K,dep[_],f[_][27],wgt[_],g[_][_],minx[_],val[_];
    vector<int> to[_];
    
    int _lca(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=L;i>=0;i--)
    	if(dep[f[x][i]]>=dep[y])
    	    x=f[x][i];
        if(x==y) return x;
        for(int i=L;i>=0;i--)
    	if(f[x][i]!=f[y][i]){
    	    x=f[x][i];
    	    y=f[y][i];
    	}
        return f[x][0];
    }
    
    void _pre1(int u,int fa){
        dep[u]=dep[fa]+1;
        f[u][0]=fa;
        for(int i=1;i<=L;i++)
    	f[u][i]=f[f[u][i-1]][i-1];
        for(int i=0;i<sz(to[u]);i++){
    		int v=to[u][i];
    		if(v==fa) continue;
    		_pre1(v,u);
        }
    }
    
    void _pre2(int u){
        for(int i=0;i<sz(to[u]);i++){
    	int v=to[u][i];
    	if(v==f[u][0]) continue;
    		_pre2(v);
    		wgt[u]+=wgt[v];
        }
    }
    
    void _init(){
        cin>>n>>m>>K;
        for(int i=1;i<=n;i++)
    	scanf("%d",&val[i]);
        
        int x,y;
        for(int i=1;i<n;i++){
    		scanf("%d%d",&x,&y);
    		to[x].pb(y); to[y].pb(x);
        }
        
        _pre1(1,0);
        for(int i=1;i<=m;i++){
    		scanf("%d%d",&x,&y);
    		wgt[x]++,wgt[y]++,wgt[_lca(x,y)]-=2;
        }
        _pre2(1);
    }
    
    void _dp(int u,int lim){
        for(int i=0;i<sz(to[u]);i++){
    		int v=to[u][i];
    		if(v==f[u][0]) continue;
    		_dp(v,lim);
        }
    
        for(int j=1;j<=n;j++){
            if(val[j]+lim<val[u]||val[j]>val[u]) continue;
            g[u][j]=0;
            for(int i=0;i<sz(to[u]);i++){
                int v=to[u][i];
                if(v==f[u][0]) continue;
                g[u][j]+=min(g[v][j],minx[v]+wgt[v]);
            }
            minx[u]=min(minx[u],g[u][j]);
        }
    }
    	
    bool _check(int lim){
        memset(g,0x3f,sizeof(g));
        memset(minx,0x3f,sizeof(minx));
        _dp(1,lim);
        return minx[1]<=K;
    }
    
    void _run(){
        int ans=0,l=0,r=1e9;
        while(l<=r){
            int mid=(l+r)>>1;
            if(_check(mid)){ r=mid-1; ans=mid; }
            else l=mid+1;
        }
        printf("%d
    ",ans);
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("x.in","r",stdin);
    #endif
        _init();
        _run();
        return 0;
    }
    
  • 相关阅读:
    绝对路径和相对路径
    基本的文件操作
    Python2和3字符编码区别
    java开发两年了,连个java代理模式都摸不透,你怎么跳槽涨薪?
    【建议收藏】阿里P7总结的Spring注解笔记,把组件注册讲的明明白白
    面试官:你说你精通SpringBoot,你给我说一下类的自动装配吧
    面试BAT问的最多的27道MyBatis 面试题(含答案和思维导图总结)
    Springboot 框架整理,建议做开发的都看看,整理的比较详细!
    直面秋招!非科班生背水一战,最终拿下阿里等大厂offer!
    写的太细了!Spring MVC拦截器的应用,建议收藏再看!
  • 原文地址:https://www.cnblogs.com/BruceW/p/13149541.html
Copyright © 2020-2023  润新知