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

「解题报告」[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 \times 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 1\le l \le r \le max\_val\}\) ,

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

若 $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
#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 to[_];

int _lca(int x,int y){
    if(dep[x]=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>n>>m>>K;
    for(int i=1;i<=n;i++)
	scanf("%d",&val[i]);
    
    int x,y;
    for(int i=1;ival[u]) continue;
        g[u][j]=0;
        for(int i=0;i>1;
        if(_check(mid)){ r=mid-1; ans=mid; }
        else l=mid+1;
    }
    printf("%d\n",ans);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("x.in","r",stdin);
#endif
    _init();
    _run();
    return 0;
}

你可能感兴趣的:(「解题报告」[luoguP6594]换寝室 (二分答案 树形DP))