[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
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值