[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;
}

 

### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交流和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键和需要复习的知识等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FSYo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值