洛谷 P4292 - [WC2010]重建计划(长链剖分+线段树)

题面传送门

我!竟!然!独!立!A!C!了!这!道!题!incredible!

首先看到这类最大化某个分式的题目,可以套路地想到分数规划,考虑二分答案 \(mid\) 并检验是否存在合法的 \(S\) 使得 \(\dfrac{\sum\limits_{e\in S}v(e)}{|S|}\ge mid\),将分母乘过去并稍微变个形可得 \(\sum\limits_{e\in S}v(e)-mid\ge 0\),也就是说我们将每条边边权都减去 \(mid\) 并检验包含 \([L,R]\) 条边的路径权值和的最大值是否 \(\ge 0\),于是现在题目转化为怎样求包含 \([L,R]\) 条边路径权值和的最大值。

我们记 \(f_{x,i}\) 为对于 \(x\) 的子树中所有深度为 \(i\) 的点 \(y\)\(x\)\(y\) 距离的最大值,那么显然有状态转移方程 \(dp_{x,i}=\max\limits_{y\in son(x)}dp_{y,i}+v(x,y)\),其中 \(v(x,y)\)\(x,y\) 之间边的边权,显然我们可以像点分治那样在遍历子树,更新 \(f_{x,i}\) 的过程中更新答案。但暴力遍历更新是 \(\mathcal O(n^2)\) 的,无法通过。考虑优化,这时候长链剖分就要派上用场了,我们建一棵全局线段树,实时维护当前 \(f_{x,i}\) 的值,我们遍历深儿子 \(y\) 的时候就直接将其 \(f_{y,i}\) 的值继承到 \(f_{x,i}\) 上,当然由于贡献中有一个 \(v(x,y)\),因此 \(f_{x,i}\) 的值应当为 \(f_{y,i}\) 加上 \(v(x,y)\),这个可以通过线段树区间加解决,即在线段树上 \([dep_x+1,mxdep_x]\) 的值全部加上 \(v(x,y)\)。遍历浅儿子 \(u\)的时候,我们就在第一次遍历时开个临时变量(vector<vector<double> >)记录下所有 \(f_{u,j}\) 的值——显然对于每个浅儿子 \(u\)\(f_{u,j}\) 只在 \(j\in[dep_u,mxdep_u]\) 时值不是 \(-\infty\),因此我们只需记录下 \(mxdep_u-dep_u+1\) 个这样的值即可,根据所有链长之和为线性可知对于所有 \(x\),我们总共最多保存 \(\mathcal O(n)\) 个这样的值。然后第二遍遍历时直接调用之前保存下来的值,在线段树上单点修改即可,贡献就随便乱搞搞,算一下即可,相当于在线段树上执行一遍区间查询。

总复杂度 \(n\log^2n\),其中一个 \(\log\) 来源于线段树,一个 \(\log\) 来源于分数规划中的二分。

代码可能要开个 O2 才能过:

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const double INF=1e12;
const double EPS=1e-4;
int n,L,R,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],val[MAXN*2+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
struct node{int l,r;double mx,lz;} s[MAXN*4+5];
void pushup(int k){s[k].mx=max(s[k<<1].mx,s[k<<1|1].mx);}
void build(int k,int l,int r){
	s[k].l=l;s[k].r=r;s[k].mx=-INF;s[k].lz=0;if(l==r) return;
	int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushdown(int k){
	if(fabs(s[k].lz)>EPS){
		s[k<<1].lz+=s[k].lz;s[k<<1].mx+=s[k].lz;
		s[k<<1|1].lz+=s[k].lz;s[k<<1|1].mx+=s[k].lz;
		s[k].lz=0;
	}
}
void add(int k,int l,int r,double v){
	if(l<=s[k].l&&s[k].r<=r){s[k].mx+=v;s[k].lz+=v;return;}
	pushdown(k);int mid=s[k].l+s[k].r>>1;
	if(r<=mid) add(k<<1,l,r,v);
	else if(l>mid) add(k<<1|1,l,r,v);
	else add(k<<1,l,mid,v),add(k<<1|1,mid+1,r,v);
	pushup(k);
}
void modify(int k,int p,double x){
	if(s[k].l==s[k].r){chkmax(s[k].mx,x);return;}
	pushdown(k);int mid=s[k].l+s[k].r>>1;
	if(p<=mid) modify(k<<1,p,x);
	else modify(k<<1|1,p,x);
	pushup(k);
}
void clear(int k,int p){
	if(s[k].l==s[k].r){s[k].mx=-INF;return;}
	pushdown(k);int mid=s[k].l+s[k].r>>1;
	if(p<=mid) clear(k<<1,p);
	else clear(k<<1|1,p);
	pushup(k);
}
double query(int k,int l,int r){
	if(l<=s[k].l&&s[k].r<=r) return s[k].mx;
	pushdown(k);int mid=s[k].l+s[k].r>>1;
	if(r<=mid) return query(k<<1,l,r);
	else if(l>mid) return query(k<<1|1,l,r);
	else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}
int dep[MAXN+5],mxdep[MAXN+5],dson[MAXN+5],ed[MAXN+5];
void dfs0(int x=1,int f=0){
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e],z=val[e];if(y==f) continue;
		dep[y]=mxdep[y]=dep[x]+1;dfs0(y,x);
		if(mxdep[y]>mxdep[x]) mxdep[x]=mxdep[y],dson[x]=y,ed[x]=z;
	}
}
double ans=-INF;
void dfs(int x,int f,double v){
	vector<pair<int,vector<double> > > t;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e],z=val[e];if(y==f||y==dson[x]) continue;
		dfs(y,x,v);vector<double> tmp;
		for(int j=dep[y];j<=mxdep[y];j++) tmp.pb(query(1,j,j)),clear(1,j);
		t.pb(mp(z,tmp));
	} if(dson[x]){
		dfs(dson[x],x,v);
		add(1,dep[x]+1,mxdep[x],ed[x]-v);
	} chkmax(ans,query(1,min(dep[x]+L,n),min(dep[x]+R,n)));
	modify(1,dep[x],0);
	for(int i=0;i<t.size();i++){
		for(int j=0;j<t[i].se.size();j++){
			double vv=t[i].fi-v+t[i].se[j];
			if(R-j-1<0) continue;
			int lwb=max(L-j-1,0),upb=R-j-1;
			chkmax(ans,query(1,min(dep[x]+lwb,n),min(dep[x]+upb,n))+vv);
		}
		for(int j=0;j<t[i].se.size();j++)
			modify(1,dep[x]+1+j,t[i].fi-v+t[i].se[j]);
	}
}
bool check(double mid){
	build(1,0,n);ans=-INF;dfs(1,0,mid);
//	printf("%.3lf\n",ans);
	return ans>-EPS;
}
int main(){
	scanf("%d%d%d",&n,&L,&R);
	for(int i=1,u,v,w;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);
		adde(u,v,w);adde(v,u,w);
	} dfs0();//check(2.5);
	double l=0,r=1e6,x=-114514.1919810;
	while(fabs(r-l)>EPS){
		double mid=(l+r)/2.0;
		if(check(mid)) x=mid,l=mid;
		else r=mid;
	} printf("%.3lf\n",x);
	return 0;
}
posted @ 2021-03-25 18:02  tzc_wk  阅读(37)  评论(0编辑  收藏  举报