[BZOJ1758] [WC2010] 重建计划 - 树分治

1758: [Wc2010]重建计划

Time Limit: 40 Sec   Memory Limit: 162 MB
Submit: 2178   Solved: 700
[ Submit][ Status][ Discuss]

Description

Input

第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号

Output

输出最大平均估值,保留三位小数

Sample Input

4
2 3
1 2 1
1 3 2
1 4 3

Sample Output

2.500

HINT

20%的数据,N<=5000
30%的数据,N<=100000,原有方案恰好为一条路径
100%的数据,N<=100000,1<=L<=U<=N-1,Vi<=1000000

Source

[ Submit][ Status][ Discuss]

HOME   Back

        树分治的题怎么都轻易地这么烦啊qwq。

upd.正解应该是树分治+单调队列 之前一直不知道自己为什么能过 现在有hack数据了 下面的做法正确性的确不能保证 向所有看过我代码的人致歉…


        树分治就不用说了,主要是work比较难写。首先我们考虑每层分治换根后,对于根x的所有子节点(删除的不算)进行dfs,求出每个点到根的距离,隶属的子树根节点(x的子节点)和对答案的贡献(注意贡献要longlong)

        然后我们考虑如何在统计答案。考虑对于一条到根的路径,如果其本身就在L-U范围内,显然求一次max。否则我们考虑的范围就是不在此子树的结点中长度在max(L-len,1)~min(U-len,maxlen)中。显然我们可以用线段树存储长度为x的最大答案和与最大答案子树不同的最大答案,可以证明只会用到这两个答案。

        时间复杂度O(nlognlogS),其中S为子树平均深度。

#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
const int L=3000005;
char _buff[L]; int _pos=-1;
void ReadIn(){fread(_buff,L,sizeof(char),stdin);}
#define fge _buff[++_pos]
inline int read(){
	int x=0,f=1; char ch=fge;
	while(ch>'9'||ch<'0')
	{if(ch=='-')f=-1;ch=fge;}
	while(ch<='9'&&ch>='0')
		x=x*10+ch-'0',ch=fge;
	return x*f;
}

const int N=100005;
struct P{
	int v,c;
	P(int _=0,int __=0)
	{v=_,c=__;}
};
vector<P>e[N];
struct Data{
	int len,ffa;ll sval;
	Data(int _=0,ll __=0,int ___=0)
	{ len=_; sval=__; ffa=___;}
	bool operator <(const Data&a)const
	{ return sval*(ll)a.len<a.sval*(ll)len;}
	Data operator +(const Data&a)const
	{ return Data(len+a.len,sval+a.sval,ffa);} 
} q[N] ,ans;
bool cmplen(const Data&a,const Data&b)
{ return a.len<b.len;}

int n,size[N],mnr,mxr,c,maxlen;
bool f[N],v[N];

void dfs(int x){
	v[x]=true;size[x]=1;int i;
	for(i=0;i<e[x].size();i++){
		int to=e[x][i].v;
		if(!f[to]&&!v[to]){
			dfs(to);
			size[x]+=size[to];
		}
	}
	v[x]=false;
}

int getweight(int x){
	dfs(x);bool move=true;
	int m=size[x],i,to,fn;
	while(move){
		move=false;
		for(i=0;i<e[x].size();i++){
			to=e[x][i].v;
			if(size[to]>size[x])continue;
			if(size[to]>m/2&&!f[to]){
				x=to;
				move=true;
				break;
			}
		}
	}
	return x;
}

void BuildData(int x,int fa,int d,ll scost){
	int i,to,cost;q[++c]=Data(d,scost,fa);
	for(v[x]=true,i=0;i<e[x].size();i++){
		to=e[x][i].v;cost=e[x][i].c;
		if(f[to]||v[to]) continue;
		BuildData(to,fa,d+1,scost+cost);
	}v[x]=false; maxlen=max(maxlen,d);
}

struct Node{
	Data larg,slar;
	Node (Data _=Data(N,0,0),Data __=Data(N,0,0))
	{ larg=_; slar=__;}
	void update(Node a,Node b){
		larg=max(a.larg,b.larg);
		if(a.larg.ffa!=larg.ffa)slar=max(slar,a.larg);
		else if(a.slar.ffa!=larg.ffa)slar=max(slar,a.slar);
		if(b.larg.ffa!=larg.ffa)slar=max(slar,b.larg);
		else if(b.slar.ffa!=larg.ffa)slar=max(slar,b.slar);
	}
} d[4*N];
int pl[N];

void buildpl(int v,int l,int r){
	d[v]=Node();int mid=(l+r)>>1;
	if(l==r){pl[l]=v;return;}
	buildpl(v<<1,l,mid);
	buildpl(v<<1|1,mid+1,r);
}

void buildtr(int v,int l,int r){
	int mid=(l+r)>>1;
	if(l==r){return;}
	buildtr(v<<1,l,mid);
	buildtr(v<<1|1,mid+1,r);
	d[v].update(d[v<<1],d[v<<1|1]);
}

Node ask(int v,int l,int r,Node&dt){
	int _l=mnr-dt.larg.len,_r=mxr-dt.larg.len;
	if(_r<=0||_l>maxlen)return Node(Data(),Data());
	_l=max(_l,l);_r=min(_r,r);
	if(_l>_r)return Node(Data(),Data());
	Node ret=Node(Data(1,N*1000,dt.larg.ffa),Data(N,0,0));int mid=(l+r)>>1;
	if(l>=_l&&r<=_r){ret.update(ret,d[v]);return ret;}
	if(_r<=mid)return ask(v<<1,l,mid,dt);
	if(_l>mid) return ask(v<<1|1,mid+1,r,dt);
	ret.update(ask(v<<1,l,mid,dt),ask(v<<1|1,mid+1,r,dt));
	return ret;
}

void work(int x){
	c=maxlen=0;v[x]=true;int i,to,cost,j;
	fill(q+1,q+size[x],Data());
	for(i=0;i<e[x].size();i++){
		to=e[x][i].v;cost=e[x][i].c;
		if(f[to]||v[to]) continue;
		BuildData(to,to,1,cost);
	} if(maxlen==0)return;
	fill(d+1,d+maxlen*2,Node());
	buildpl(1,1,maxlen);
	for(i=1;i<=c;i++){
		d[pl[q[i].len]].update(d[pl[q[i].len]],Node(q[i],Data(N,0,0)));
	} buildtr(1,1,maxlen);
	for(i=1;i<=maxlen;i++){
		Node th=ask(1,1,maxlen,d[pl[i]]);
		if(th.larg.len)
		ans=max(ans,th.slar+d[pl[i]].larg);
		if(i>=mnr&&i<=mxr)
		ans=max(ans,d[pl[i]].larg);
	}
}

void TreeConquer(int x){
	x=getweight(x);work(x);int i,to;
	for(f[x]=true,i=0;i<e[x].size();i++){
		to=e[x][i].v;
		if(f[to]) continue;
		TreeConquer(to);
	}
}

int main(){
	ReadIn();n=read();int i,u,v,c;
	for(mnr=read(),mxr=read(),i=1;i<n;i++){
		u=read(),v=read(),c=read();
		e[u].push_back(P(v,c));
		e[v].push_back(P(u,c));
	} ans=Data(N,0,0); TreeConquer(1);
	printf("%.3lf\n",(double)ans.sval/ans.len);
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值