[bzoj1758] 重建计划 [二分答案+点分治+单调队列]

传送门

原文:https://blog.csdn.net/icefox_zhx/article/details/79090839 

首先求一个最优比率的东西,我们二分答案,转化成判定是否存在一条合法路径使得边权和≥0.

然后这个东西我们点分治去做。每次计算过x的合法路径的最大值时,为了避免不合法,我们一个子树一个子树的做。

我们处理出g[i],表示目前这棵子树深度为i的点的最大距离,tmp[i]表示之前做过的子树深度为i的点的最大距离。

我们每次用g[i]去询问tmp,也就是要在tmp[l-i,r-i]中选一个最大值,我们发现这就是一个滑动的窗口,可以用单调队列来维护。

然后为了保证复杂度,我们要按子树深度(大小)从小到大来做。清零要手动清。

为了卡常,我们二分答案要在每次分治里面分,最小值直接从ans开始枚举,子树大小< L时就可以不做了


#include<bits/stdc++.h>
#define N 100050
#define inf 0x3fffffff
#define eps 1e-4
using namespace std;
int read(){
	int cnt = 0; char ch = 0;
	while(!isdigit(ch)) ch = getchar();
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt;
}
int first[N], nxt[N*2], to[N*2], w[N*2], tot;
void add(int x,int y,int z){
	nxt[++tot] = first[x], first[x] = tot;
	to[tot] = y, w[tot] = z;
}
int n,L,R,Siz,Maxdep;
int dep[N], siz[N], fa[N], Maxson[N]; 
int rt, sum, vis[N]; double ans, mid, g[N], tmp[N], dis[N];
struct Son{ int id, val;}son[N];
bool cmp(Son a,Son b){ return siz[a.id] < siz[b.id];}
void dfs(int u,int f){
	siz[u] = 1;
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t==f || vis[t]) continue;
		fa[t] = u, dep[t] = dep[u] + 1;
		dfs(t,u); siz[u] += siz[t];
	}
}
void getrt(int u){
	Maxson[u] = 0; 
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t==fa[u] || vis[t]) continue;
		getrt(t); Maxson[u] = max(Maxson[u], siz[t]);
	} Maxson[u] = max(Maxson[u], Siz - siz[u]);
	if(Maxson[u] < Maxson[rt]) rt = u;
}
void getdis(int u){
	g[dep[u]] = max(g[dep[u]], dis[u]);
	Maxdep = max(Maxdep, dep[u]);
	for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(vis[t] || t==fa[u]) continue;
		dis[t] = dis[u] + w[i] - mid; getdis(t);
	}
}
bool check(int x){
	int Max = 0, flag = 0; tmp[0] = 0;
	deque<pair<double,int> > q;
	for(int d=1; d<=sum; d++){
		int pos = son[d].id; if(vis[pos]) continue;
		dis[pos] = son[d].val - mid;
		Maxdep = 0; getdis(pos);
		int now = L - Maxdep; now = max(now, 0);
		for(int i=Maxdep; i>=1; i--){
			while(now <= Max && now + i <= R){
				while(!q.empty() && tmp[now] > q.back().first) q.pop_back();
				q.push_back(make_pair(tmp[now], now)); now ++;
			}
			while(!q.empty() && q.front().second + i < L) q.pop_front();
			if(!q.empty() && q.front().first + g[i] >= -eps){ flag = 1; break;}
		}
		for(int i=Maxdep; i>=1; i--){
			tmp[i] = max(tmp[i], g[i]), g[i] = -inf;
		} Max = max(Max, Maxdep);
		if(flag) break;
	}
	for(int i=0;i<=Max;i++) tmp[i] = -inf;
	return flag;
}
void Solve(int x){
	vis[x] = 1; fa[x] = 0; dep[x] = 0; dfs(x,0);
	if(siz[x] < L) return;
	double l = ans, r = 1000000; sum = 0;
	for(int i=first[x];i;i=nxt[i]){
		int t = to[i]; if(vis[t]) continue;
		son[++sum] = (Son){t, w[i]};
	} sort(son+1, son+sum+1, cmp);
	while((r-l) > eps){
		mid = (l+r) / 2;
		if(check(x)) l = mid;
		else r = mid;
	} ans = l;
	for(int i=first[x];i;i=nxt[i]){
		int t = to[i]; if(vis[t]) continue;
		rt = 0, Siz = siz[t]; getrt(t); Solve(rt);
	}
}
int main(){
	n = read(), L = read(), R = read();
	for(int i=1; i<n; i++){
		int x = read(), y = read(), z = read();
		add(x,y,z); add(y,x,z);
	} 
	for(int i=0;i<=n;i++) g[i] = tmp[i] = -inf;
	dfs(1,0); rt = 0, Maxson[rt] = inf, Siz = n; 
	getrt(1); Solve(rt); printf("%0.3lf",ans); return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值