Luogu P4292 [WC2010]重建计划

Luogu P4292 [WC2010]重建计划
二分答案

\[Ans\leq \frac{\sum_{e\in S}v(e)}{|S|}\\ \therefore \sum_{e\in S} (v(e)-Ans)\geq0 \]

然后找边权最大的点对,判断是否大于0

法1:长链剖分+线段树

考虑最简单的DP,\(f[i][j]\)表示\(i\)号点深度为\(j\)的最大和,和深度有关,可以用长链剖分

考虑如何维护边权和

当轻链合并到重链上时,时间复杂度只能与轻链长度有关

要求区间最大值,可以用线段树维护,一定要先求最大值,再进行修改

另外还需要区间加,单点改,直接在线段树上维护重链信息,千万不能新开数组,区间加没法做(会导致时间复杂度和重链长度挂钩)

#include<bits/stdc++.h>
#define ll long long
const int fj=1e5;
const ll INF=1e18;
using namespace std;


const int N=1e5+5,M=1e6+5; 
int n,L,R,dep[N],E1[N],E2[N],E3[N],dfn[N],son[N];
ll c[M],t[M],ret;
vector<int>V1[N];
struct A{int v; ll w;};
vector<A>V[N];
inline void up(int p) {
	c[p]=max(c[p<<1],c[p<<1|1]);
}
inline void down(int p) {
	if(t[p]) {
		t[p<<1]+=t[p],c[p<<1]+=t[p];
		t[p<<1|1]+=t[p],c[p<<1|1]+=t[p];
		t[p]=0;
	}
}
void upd(int p,int l,int r,int x,ll k) {
	if(l==r) {
		c[p]=k; return;
	}
	down(p);
	int mid=l+r>>1;
	if(x<=mid) upd(p<<1,l,mid,x,k);
		else upd(p<<1|1,mid+1,r,x,k);
	up(p);
}

void add(int p,int l,int r,int x,int y,ll k) {
	if(l==x&&r==y) {
		c[p]+=k,t[p]+=k;
		return;
	}
	down(p);
	int mid=l+r>>1;
	if(y<=mid) add(p<<1,l,mid,x,y,k);
		else if(x>mid) add(p<<1|1,mid+1,r,x,y,k);
			else {
				add(p<<1,l,mid,x,mid,k);
				add(p<<1|1,mid+1,r,mid+1,y,k);
			}
	up(p);
}
ll sum(int p,int l,int r,int x,int y) {
	if(x>y) return -INF;
	if(l==x&&r==y) return c[p];
	down(p);
	int mid=l+r>>1;
	if(y<=mid) return sum(p<<1,l,mid,x,y);
	if(x>mid) return sum(p<<1|1,mid+1,r,x,y);
	return max(sum(p<<1,l,mid,x,mid),sum(p<<1|1,mid+1,r,mid+1,y)); 
}
void dhs(int fa,int u) {
	upd(1,1,n,dfn[u],0);
	for(A v:V[u]) {
		if(v.v==son[u]) {
			dhs(u,son[u]);
			add(1,1,n,dfn[u]+1,dfn[u]+dep[u]-1,v.w);
			ret=max(ret,sum(1,1,n,max(dfn[u]+L-1,dfn[u]),min(dfn[u]+R-1,dfn[u]+dep[u]-1)));
		}
	}
	for(A v:V[u]) {
		if(v.v!=son[u]&&v.v!=fa) {
			dhs(u,v.v);
			for(int i=1;i<=dep[v.v];i++) {
				ret=max(ret,sum(1,1,n,max(dfn[u]+L-i-1,dfn[u]),min(dfn[u]+R-i-1,dfn[u]+dep[u]-1))+sum(1,1,n,dfn[v.v]+i-1,dfn[v.v]+i-1)+v.w);
			}
			for(int i=1;i<=dep[v.v];i++) {
				ll t=sum(1,1,n,dfn[v.v]+i-1,dfn[v.v]+i-1);
				if(sum(1,1,n,dfn[u]+i,dfn[u]+i)<t+v.w) {
					upd(1,1,n,dfn[u]+i,t+v.w);	
				}
			}
		}
	}	
}
void bld(int p,int l,int r) {
	t[p]=0,c[p]=-INF;
	if(l==r) return;
	int mid=l+r>>1;
	bld(p<<1,l,mid),bld(p<<1|1,mid+1,r);
}
bool check(ll mid) {
	for(int i=1;i<=n;i++) {
		V[i].clear();
		vector<A>().swap(V[i]);
	}
	for(int i=1;i<n;i++) {
		int u=E1[i],v=E2[i],k=E3[i];
		V[u].push_back((A){v,(ll)k*fj-mid});
		V[v].push_back((A){u,(ll)k*fj-mid});
	}
	bld(1,1,n);
	ret=-INF;
	dhs(0,1);
	return ret>=0;
}
void dfs(int fa,int u) {
	dep[u]=1; 
	for(int v:V1[u]) {
		if(v!=fa) {
			dfs(u,v);
			dep[u]=max(dep[u],dep[v]+1);
			if(dep[son[u]]<dep[v]) son[u]=v;
		}
	}
}
int sgn;
void dgs(int fa,int u) {
	dfn[u]=++sgn;
	if(son[u]) dgs(u,son[u]);
	for(int v:V1[u]) {
		if(v!=fa&&v!=son[u]) {
			dgs(u,v);
		}
	}
}
int main(){
	scanf("%d%d%d",&n,&L,&R); L++,R++;
	for(int i=1;i<n;i++) {
		scanf("%d%d%d",&E1[i],&E2[i],&E3[i]);
		V1[E1[i]].push_back(E2[i]);
		V1[E2[i]].push_back(E1[i]);
	}
	dfs(0,1),dgs(0,1);
	ll l=0,r=1e11,mid,ans;
	while(l<=r) {
		mid=l+r>>1;
		if(check(mid)) {
			ans=mid,l=mid+1;
		} else r=mid-1;
	}
	printf("%.3lf\n",(double)ans/fj);
	return 0;
}

法2

点分治+单调队列

点分治后暴力求出深度数组,合并可以按深度排序后采用单调队列(滑动窗口)

posted @ 2021-03-23 12:37  wwwsfff  阅读(44)  评论(0)    收藏  举报