【线段树合并+虚树/启发式合并/欧拉序】LOJ2722 NOI2018情报中心

【题目】
原题地址
给定一棵带边权树和树上的 m m m条链,每条链有一个花费 c i c_i ci,两条有边交的链的价值定义为:链并的边权和-两条链的花费。求最大价值。
(需要用一个 l o g log log的做法)

【题目分析】
这题是 S C SC SC在看,我跑过去凑热闹,然后我们搞了一个星期 … \dots 两个部分都搞出了一个可行的做法,然后看题解 … \dots 震惊,还能这样做。
(update:如果有大佬能把第二个常数巨大的代码卡过,感激不尽!)

【解题思路】
题目给出了 S 1 ( l c a 不 同 ) S1(lca不同) S1(lca) S 2 ( l c a 相 同 ) S2(lca相同) S2(lca)两个部分,我们可以朝这两个方向思考。

考虑 S 1 S1 S1,观察到对于 l c a lca lca不同的两条链,两条链的交必然是直上直下的一段,即链交深度是连续的,因此我们可以把一条链拆成两条直上直下的链考虑。

现在对于两条链 ( u 1 , v 1 , l c a 1 ) 和 ( u 2 , v 2 , l c a 2 ) (u_1,v_1,lca_1)和(u_2,v_2,lca_2) (u1,v1,lca1)(u2,v2,lca2),考虑枚举链交深度较深的交点 x x x,那么显然这两条链的下端点一定在 x x x的不同儿子子树中。
我们要求的答案 a n s = l e n 1 + l e n 2 − c o s t 1 − c o s t 2 − d e p x + m a x d e p ( l c a 1 , l c a 2 ) ans=len_1+len_2-cost_1-cost_2-dep_x+maxdep(lca_1,lca_2) ans=len1+len2cost1cost2depx+maxdep(lca1,lca2)(这里深度为带权深度。)

这里一开始的想法是枚举一个点作为链交的一个交点,可以通过分析得到一个通过线段树合并以及多次查询的 O ( m l o g 2 n ) O(mlog^2n) O(mlog2n)的做法,但是略为复杂在次不作阐述。

我们对每个点记 f i , j f_{i,j} fi,j表示下端点在 i i i子树内,上端不带权深度为 j j j的最大 l e n − c o s t len-cost lencost,可以用启发式合并或线段树合并解决这个 d p dp dp

具体来说,我们需要按不带权深度从小到大为序建出线段树。
然后在 u 1 u_1 u1的线段树上插入 l c a 1 lca_1 lca1,在 u 2 u_2 u2的线段树上插入 l c a 2 lca_2 lca2,合并两棵线段树时,深度小的节点要在深度大的区间中统计贡献,即对于两棵树 x , y x,y x,y,要用( x x x l s o n lson lson y y y r s o n rson rson)以及( y y y l s o n lson lson x x x r s o n rson rson)分别贡献答案。

那么这一部分的时间复杂度是 O ( m l o g n ) O(mlogn) O(mlogn)的。

现在考虑 S 2 S2 S2,这里两条链的交可能不是直上直下的,因此比较难处理。

一个比较明显的方向是枚举两条链的公共 l c a lca lca
我们对整颗树进行轻重链剖分,对于每个节点,记录子树中除重儿子外,所有轻儿子的最大 l e n − c o s t len-cost lencost,那么对于每个节点如果要算出答案,就需要在重儿子的部分进行dfs,我们用线段树合并处理这个问题,这样处理起来是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的。

S C SC SC给出一种思路是处理出整颗树的欧拉序,对于每个节点已经知道了它在欧拉序中出现的位置,这样的话知道每条链端点的欧拉序。

观察到选择两条链,实际上就是在一个节点选择它两棵子树中的不同的链端点,再找到对应的端点进行运算,我们同样可以考虑用线段树合并进行优化。

具体实现我们可以对于每个点维护最大的 ( 到 链 l c a 距 离 − c o s t ) (到链lca距离-cost) (lcacost),然后类似 S 1 S1 S1的做法左右进行贡献。由于我太蒻,这部分我没有特别听清楚,不过大概YY了一下应该是可行的。复杂度是 O ( m l o g n ) O(mlogn) O(mlogn)的。
(update:这个做法在LOJ上得到了95分,第6个点被卡TLE了,欧拉序有一些细节问题需要处理,比方说合并上在根节点要特判之类的。)

第三种思路,观察到一个性质:链并的两倍=两条链长+ d i s ( u 1 , u 2 ) + d i s ( v 1 , v 2 ) dis(u_1,u_2)+dis(v_1,v_2) dis(u1,u2)+dis(v1,v2)

那么我们枚举 u 1 , u 2 u_1,u_2 u1,u2 l c a lca lca然后用树剖+线段树强行维护,然后选出来自不同儿子子树的两个 u u u,使得它们对应 v v v的距离- c o s t cost cost+ u 的 深 度 u的深度 u- 2 ∗ l c a u 2*lca_u 2lcau最大,而 l c a u lca_u lcau是个常数,那么我们通过附加点连边权为 链 长 − c o s t 链长-cost cost的边,就可以转化为最远点对问题,这个问题我们可以通过 d f s dfs dfs直接合并。

这里实现的时候我们需要建出虚树然后自下而上合并,也可以直接启发式合并。复杂度是 O ( m l o g n ) O(mlogn) O(mlogn)的。

据说的还有一种点分治的做法,考虑所有过重心的链,转化为过一个点的情况,时间复杂度是一样的,但是还要加一个 O ( n l o g n ) O(nlogn) O(nlogn)的点分,问题不大。

总之这道题十分复杂,写起来也可能会很乱,需要十分强大的分析能力及代码能力。

写完以后再次感受到了 n a m e s p a c e namespace namespace的好处。

【参考代码】
我的:

#include
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;

typedef long long LL;
typedef pair<LL,LL> pll;
const LL INF=(LL)1000000000000000000;
const int N=5e4+10;
int n,m,ind,tot;
int top[N],fa[N],dep[N],siz[N],head[N],son[N],dfn[N];
LL ans,len[N],c[N<<2];
vector<int>st[N];
map<int,LL>mp[N];

LL read()
{
	LL ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

struct data{int x,y,z;LL c,len;}dat[N<<1];
struct Tway{int v,w,nex;}e[N<<1];
void add(int u,int v,int w){e[++tot]=(Tway){v,w,head[u]};head[u]=tot;}

void dfs1(int x,int dp,LL l)
{
	dep[x]=dp;len[x]=l;siz[x]=1;
	for(int i=head[x];i;i=e[i].nex)
	{
		int v=e[i].v;
		dfs1(v,dp+1,l+e[i].w);
		siz[x]+=siz[v];
		if(siz[son[x]]<siz[v]) son[x]=v;
	}
}

void dfs2(int x,int tp)
{
	top[x]=tp;dfn[x]=++ind;
	if(son[x]) dfs2(son[x],tp);
	for(int i=head[x];i;i=e[i].nex)
	{
		int v=e[i].v;
		if(v==son[x]) continue;
		dfs2(v,v);
	}
}

int lca(int x,int y)
{
	while(top[x]^top[y])
	{
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

void totinit()
{
	n=read();

	ans=-INF;tot=ind=0;
	for(int i=1;i<=n;++i) 
		head[i]=son[i]=0,st[i].clear(),mp[i].clear();

	for(int i=1;i<n;++i)
	{
		int u=read(),v=read(),w=read();
		fa[v]=u;add(u,v,w);
	}
	dfs1(1,1,0);dfs2(1,1);
	//for(int i=1;i<=n;++i) printf("%d %d %d\n",siz[i],fa[i],son[i]); puts(""); 
	
	m=read();
	for(int i=1;i<=m;++i)
	{
		dat[i].x=read();dat[i].y=read();dat[i].c=read();
		dat[i].z=lca(dat[i].x,dat[i].y);
		dat[i].len=len[dat[i].x]+len[dat[i].y]-2*len[dat[i].z];
		//printf("%d %d %d %lld\n",dat[i].x,dat[i].y,dat[i].z,dat[i].len);
		if(dat[i].x==dat[i].y) continue;
		if(dat[i].x>dat[i].y) swap(dat[i].x,dat[i].y);
		if(mp[dat[i].x].count(dat[i].y))
		{
			ans=max(ans,dat[i].len-mp[dat[i].x][dat[i].y]-dat[i].c);
			mp[dat[i].x][dat[i].y]=min(mp[dat[i].x][dat[i].y],dat[i].c);
		}
		else mp[dat[i].x][dat[i].y]=dat[i].c;
		st[dat[i].z].pb(i);
		c[i]=dat[i].len-2*dat[i].c+len[dat[i].y];
		c[i+m]=dat[i].len-2*dat[i].c+len[dat[i].x];
	}
	//for(int i=1;i<=2*m;++i) printf("%lld ",c[i]); puts("");
}

namespace S1
{
	pll t[N*40],nw;
	int sz,ls[N*40],rs[N*40],rt[N];
	LL ddd;

	void init()
	{
		sz=0;
		for(int i=1;i<=n;++i) rt[i]=0;
	}

	void newnode(int &x){x=++sz;t[x]=mkp(-INF,-INF);ls[x]=rs[x]=0;}
	pll cmp(pll x,pll y){return mkp(max(x.fi,y.fi),max(x.se,y.se));}
	void insert(int &x,int l,int r,int p)
	{
		//printf("%d %d %d\n",x,l,r);
		if(!x) newnode(x);
		if(l==r){t[x]=cmp(t[x],nw);return;}
		int mid=(l+r)>>1;
		if(p<=mid) insert(ls[x],l,mid,p);
		else insert(rs[x],mid+1,r,p);
		t[x]=cmp(t[ls[x]],t[rs[x]]);
		//printf("%lld %lld\n",t[x].fi,t[x].se);
	}

	void update(int x,int l,int r,int p)
	{
		if(!x) return;
		if(l==r){t[x]=nw;return;}
		int mid=(l+r)>>1;
		if(p<=mid) update(ls[x],l,mid,p);
		else update(rs[x],mid+1,r,p);
		t[x]=cmp(t[ls[x]],t[rs[x]]);
		//printf("%lld %lld\n",t[x].fi,t[x].se);
	}

	void merge(int &x,int y,int l,int r,LL a,LL b)
	{
		ans=max(ans,max(t[x].se+a,t[x].fi+b)-ddd);
		if(!y) return;
		if(!x){x=y;return;}
		if(l==r){t[x]=cmp(t[x],t[y]);return;}
		int mid=(l+r)>>1;
		merge(ls[x],ls[y],l,mid,a,max(b,t[rs[y]].se));
		merge(rs[x],rs[y],mid+1,r,max(a,t[ls[y]].fi),b);
		t[x]=cmp(t[ls[x]],t[rs[x]]);
	}

	void calc(int x,int l,int r,LL a,LL b)
	{
		ans=max(ans,max(t[x].fi+a,+t[x].se+a)-ddd);
		if(!x || l==r) return;
		int mid=(l+r)>>1;
		calc(ls[x],l,mid,a,max(b,t[rs[x]].se));
		calc(rs[x],mid+1,r,max(a,t[ls[x]].fi),b);
	}

	void updata(int p)
	{
		nw=mkp(-INF,-INF);
		update(rt[p],1,n,dep[p]);ddd=len[p];
		calc(rt[p],1,n,-INF,-INF);
		for(int i=head[p];i;i=e[i].nex)
		{
			int v=e[i].v;
			updata(v);nw=mkp(-INF,-INF);
			update(rt[v],1,n,dep[p]);ddd=len[p];
			merge(rt[p],rt[v],1,n,-INF,-INF);
		}
	}

	void solve()
	{
		init();t[0]=mkp(-INF,-INF);
		for(int i=1;i<=m;++i)
		{
			if(dat[i].x==dat[i].y) continue;
			nw=mkp(dat[i].len-dat[i].c,dat[i].len-dat[i].c+len[dat[i].z]);
			//printf("%lld %lld\n",nw.fi,nw.se);
			insert(rt[dat[i].x],1,n,dep[dat[i].z]);
			insert(rt[dat[i].y],1,n,dep[dat[i].z]);
		}
		updata(1);
	}
};

namespace S2
{
	int A[N],B[N],pl[N<<2],stk[N];
	LL ddd,L[N];
	vector<int>vt[N];

	bool cmp(int x,int y){return dfn[x]<dfn[y];}
	void addedge(int x,int y){vt[x].pb(y);}
	void build(int x)
	{
		pl[0]=0;
		for(int i=0;i<(int)st[x].size();++i) 
			pl[++pl[0]]=dat[st[x][i]].x,pl[++pl[0]]=dat[st[x][i]].y;
		sort(pl+1,pl+pl[0]+1,cmp);
		//printf("%d\n",pl[0]);
		int tp=1;stk[tp]=x;
		for(int i=1;i<=pl[0];++i)
		{
			int t=lca(stk[tp],pl[i]),now=pl[i];
			while(dep[stk[tp]]>dep[t])
			{
				if(dep[stk[tp-1]]<=dep[t])
				{
					addedge(t,stk[tp--]);
					if(stk[tp]^t) stk[++tp]=t;
					break;
				}
				addedge(stk[tp-1],stk[tp]);--tp;
			}
			if(stk[tp]^now) stk[++tp]=now;
		}
		while(--tp) addedge(stk[tp],stk[tp+1]);
	}

	LL getdis(int x,int y)
	{
		int cx=x>m?dat[x-m].y:dat[x].x,cy=y>m?dat[y-m].y:dat[y].x;
		return len[cx]+len[cy]-2*len[lca(cx,cy)]+c[x]+c[y];
	}

	void insp(int p,int x,int y)
	{
		//printf("%d %d %d\n",p,x,y);
		if(!A[x]) A[x]=y;
		else if(!B[x])
		{
			B[x]=y;L[x]=getdis(A[x],B[x]);
			if(x^p) ans=max(ans,L[x]/2-len[x]);
		}
		else
		{
			int a=A[x],b=B[x];LL l=L[x],las;
			if((las=getdis(A[x],y))>l) l=las,a=A[x],b=y;
			if((las=getdis(B[x],y))>l) l=las,a=y,b=B[x];
			A[x]=a;B[x]=b;L[x]=l;
			if(x^p) ans=max(ans,L[x]/2-len[x]);
		}
		//printf("%d %d %lld\n",A[x],B[x],L[x]);
	}

	void init(int x){A[x]=B[x]=0;L[x]=-INF;vt[x].clear();}

	void merge(int x,int y,LL nw)
	{
		//printf("%d %d %lld\n",x,y,nw);
		int a=A[x],b=B[x];
		if(!a && !b) {A[x]=A[y];B[x]=B[y];L[x]=L[y]; return;}
		LL l=L[x],las;
		for(int i=0;i<=1;++i) for(int j=0;j<=1;++j)
		{
			int X=i?A[x]:B[x];
			int Y=j?A[y]:B[y];
			if(!X || !Y) continue;
			ans=max(ans,getdis(X,Y)/2-nw);
			if((las=getdis(X,Y))>l) l=las,a=X,b=Y;
		}
		if(L[y]>l) a=A[y],b=B[y],l=L[y];
		A[x]=a,B[x]=b,L[x]=l;
		//printf("%d %d %d\n",A[x],B[x],L[x]);
	}	


	void dfs(int x,int r)
	{
		//printf("%d\n",vt[x].size());
		for(int i=0;i<(int)vt[x].size();++i)
		{
			int v=vt[x][i];dfs(v,r);
			//printf("%d %d %d\n",x,v,r);
			if(x^r) merge(x,v,len[x]);
			init(v);
		}
	}
	
	void query(int x)
	{
		for(int i=0;i<(int)st[x].size();++i)
		{
			if(dat[st[x][i]].x==dat[st[x][i]].y) continue;
			insp(x,dat[st[x][i]].x,st[x][i]+m);
			insp(x,dat[st[x][i]].y,st[x][i]);
		}
		dfs(x,x);init(x);
	}

	void solve()
	{
		for(int i=1;i<=n;++i) L[i]=-INF;
		for(int i=1;i<=n;++i)
			if(st[i].size()>1) build(i),query(i);
	}
};

void solve()
{
	totinit();
	S1::solve();
	S2::solve();
	if(ans<=-1000000000000000) puts("F");
	else printf("%lld\n",ans);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("LOJ2722.in","r",stdin);
	freopen("LOJ2722.out","w",stdout);
#endif
	int T=read();	
	while(T--) solve();

	return 0;
}

S C SC SC的:

#include
#define mid (tl+tr)>>1
#define pbk push_back
#define clz __builtin_clz
using namespace std;
typedef long long ll;

const int maxn = 5e4 + 10;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;

inline char Getchar()
{
	static char c[100000],*p1=c,*p2=c;
	return p1==p2&&(p2=(p1=c)+fread(c,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline ll read()
{
	ll x=0;char c=Getchar();
	for(;c<'0'||c>'9';c=Getchar());
	for(;c>='0'&&c<='9';c=Getchar()) x=x*10+(c^48);
	return x;
}
void write(ll x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
}
inline ll Max(ll a,ll b){return a<b?b:a;}

int n,m,en,head[maxn];ll fans;
int fa[maxn][20],dep[maxn];ll dis[maxn];
vector<int> elr[maxn];int elrn;

struct RMQ{
	ll mi[20][maxn*2];

	ll &operator [](int p){return mi[0][p];}

	void init()
	{
		int i,j;
		for(i=1;(1<<i)<=2*n;i++)
		{
			for(j=0;j+(1<<i)<=2*n;j++) mi[i][j]=min(mi[i-1][j],mi[i-1][j+(1<<(i-1))]);
		}
	}

	inline ll ask(int l,int r)//notice! array = [l,r)
	{
		int d=31-clz(r-l);
		//printf("RMQ ask %d %d ans=%lld\n",l,r,-min(mi[d][l],mi[d][r-(1<
		return -min(mi[d][l],mi[d][r-(1<<d)]);
	}
}R;

struct edge{
	int v,l,n;
}e[maxn<<1];

namespace Sub
{
	ll x,dmx,did,ndis;
	bool renew;

	struct node{
		int lc,rc;
		ll mx,mxmi,mimx;
		ll smx,smxmi,smimx;
		int mxid,mxmiid,mimxid;

		inline void renew(ll wdis,int id,int tl,int tr)
		{
			if(dmx>mx) 
			{
				if(id!=mxid) smx=mx,smxmi=mxmi,smimx=mimx;
				mx=dmx;mxmi=mimx=R.ask(tl,tr)+dmx;
				mxid=mxmiid=mimxid=id;
			}
			else if(dmx>smx)
			{
				smx=dmx;smxmi=smimx=R.ask(tl,tr)+dmx;
			}
			fans=max(fans,mx+smx+R.ask(tl,tr)-ndis+(wdis<<1));
		}

		inline ll operator + (const node &s)
		{
			ll ans=-INFLL;
			if(mxid==s.mimxid) ans=Max(ans,Max(mx+s.smimx,smx+s.mimx));
			else               ans=Max(ans,mx+s.mimx);
			if(mxmiid==s.mxid) ans=Max(ans,Max(mxmi+s.smx,smxmi+s.mx));
			else               ans=Max(ans,mxmi+s.mx);
			return ans;
		}

		void init(){mx=mxmi=mimx=smx=smxmi=smimx=-INFLL;mxid=mxmiid=mimxid=lc=rc=0;}
	}nt[maxn*80];int ntn;

	struct tree{
		int rt;ll wdis;

		inline void up(int p,int tl,int tr)
		{
			int lc=nt[p].lc,rc=nt[p].rc;ll tmp;
			if(nt[lc].mx>nt[rc].mx)
			{
				nt[p].mx=nt[lc].mx;nt[p].mxid=nt[lc].mxid;
				nt[p].smx=Max(nt[lc].smx,(nt[rc].mxid==nt[p].mxid?nt[rc].smx:nt[rc].mx));
			}
			else 
			{
				nt[p].mx=nt[rc].mx;nt[p].mxid=nt[rc].mxid;
				nt[p].smx=Max(nt[rc].smx,(nt[lc].mxid==nt[p].mxid?nt[lc].smx:nt[lc].mx));
			}

			if(Max(nt[lc].mxmi,nt[rc].mxmi)<(tmp=Max(-INFLL,nt[lc].mx+R.ask(mid,tr))))
			{
				nt[p].mxmi=tmp;nt[p].mxmiid=nt[lc].mxid;
				nt[p].smxmi=Max(Max((nt[lc].mxmiid==nt[p].mxmiid?nt[lc].smxmi:nt[lc].mxmi)
								   ,(nt[rc].mxmiid==nt[p].mxmiid?nt[rc].smxmi:nt[rc].mxmi))
								   ,Max(-INFLL,nt[lc].smx+R.ask(mid,tr)));
			}
			else if(nt[lc].mxmi>nt[rc].mxmi)
			{
				nt[p].mxmi=nt[lc].mxmi;nt[p].mxmiid=nt[lc].mxmiid;
				nt[p].smxmi=Max(Max(nt[lc].smxmi
					,(nt[rc].mxmiid==nt[p].mxmiid?nt[rc].smxmi:nt[rc].mxmi))
					,(nt[lc].mxid==nt[p].mxmiid?Max(-INFLL,nt[lc].smx+R.ask(mid,tr)):tmp));
			}
			else
			{
				nt[p].mxmi=nt[rc].mxmi;nt[p].mxmiid=nt[rc].mxmiid;
				nt[p].smxmi=Max(Max(nt[rc].smxmi
					,(nt[lc].mxmiid==nt[p].mxmiid?nt[lc].smxmi:nt[lc].mxmi))
					,(nt[lc].mxid==nt[p].mxmiid?Max(-INFLL,nt[lc].smx+R.ask(mid,tr)):tmp));
			}

			if(Max(nt[lc].mimx,nt[rc].mimx)<(tmp=Max(-INFLL,R.ask(tl,mid)+nt[rc].mx)))
			{
				nt[p].mimx=tmp;nt[p].mimxid=nt[lc].mxid;
				nt[p].smimx=Max(Max((nt[lc].mimxid==nt[p].mimxid?nt[lc].smimx:nt[lc].mimx)
								   ,(nt[rc].mimxid==nt[p].mimxid?nt[rc].smimx:nt[rc].mimx))
								   ,Max(-INFLL,R.ask(tl,mid)+nt[rc].smx));
			}
			else if(nt[lc].mimx>nt[rc].mimx)
			{
				nt[p].mimx=nt[lc].mimx;nt[p].mimxid=nt[lc].mimxid;
				nt[p].smimx=Max(Max(nt[lc].smimx
					,(nt[rc].mimxid==nt[p].mimxid?nt[rc].smimx:nt[rc].mimx))
					,(nt[rc].mxid==nt[p].mimxid?Max(-INFLL,R.ask(tl,mid)+nt[rc].smx):tmp));
			}
			else
			{
				nt[p].mimx=nt[rc].mimx;nt[p].mimxid=nt[rc].mimxid;
				nt[p].smimx=Max(Max(nt[rc].smimx
					,(nt[lc].mimxid==nt[p].mimxid?nt[lc].smimx:nt[lc].mimx))
					,(nt[rc].mxid==nt[p].mimxid?Max(-INFLL,R.ask(tl,mid)+nt[rc].smx):tmp));
			}
			if(renew) fans=Max(fans,nt[lc]+nt[rc]-ndis+(wdis<<1));
		}

		int tedit(int p,int tl,int tr)
		{
			if(!p) p=ntn++,nt[p].init();
			if(tl==tr-1) 
			{
				nt[p].renew(wdis,did,tl,tr);
				return p;
			}
			if(x<mid) nt[p].lc=tedit(nt[p].lc,tl,mid);
			else      nt[p].rc=tedit(nt[p].rc,mid,tr);
			renew=true;up(p,tl,tr);return p;
		}

		void edit(ll X,ll DMX,ll DID,ll NDIS)
		{
			x=X;dmx=DMX;did=DID;ndis=NDIS;
			rt=tedit(rt,0,maxn<<1);
		}

		int merge(int x,int y,int tl,int tr)
		{
			if(!x) return y;if(!y) return x;
			if(tl==tr-1)
			{
				nt[x].mx=Max(nt[x].mx,nt[y].mx);nt[x].mxmi=nt[x].mimx=R.ask(tl,tr)+nt[x].mx;
				return x;
			}

			fans=Max(fans,nt[nt[x].lc]+nt[nt[y].rc]-ndis+(wdis<<1));
			fans=Max(fans,nt[nt[y].lc]+nt[nt[x].rc]-ndis+(wdis<<1));

			nt[x].lc=merge(nt[x].lc,nt[y].lc,tl,mid);
			nt[x].rc=merge(nt[x].rc,nt[y].rc,mid,tr);
			renew=false;up(x,tl,tr);
			return x;
		}

		inline void mrg(tree &s,ll NDIS){ndis=NDIS;rt=merge(rt,s.rt,0,maxn<<1);}
	};
}

namespace Main
{
	struct node{
		int lc,rc;
		ll lmx,rmx;
		Sub::tree T;
		void init(){lmx=rmx=-INFLL;T.rt=0;lc=rc=0;}
	}nt[maxn*40];int ntn;

	ll x,dl,dr;bool renew;
	ll L;
	Sub::tree *Tmp;

	struct tree{
		int rt;ll ndis;

		inline void up(int p)
		{
			int lc=nt[p].lc,rc=nt[p].rc;
			nt[p].lmx=Max(nt[lc].lmx,nt[rc].lmx);
			nt[p].rmx=Max(nt[lc].rmx,nt[rc].rmx);
			if(renew) fans=Max(fans,nt[lc].lmx+nt[rc].rmx-ndis);
		}

		int tedit(int p,int tl,int tr)
		{
			if(!p) p=ntn++,nt[p].init();
			if(tl==tr-1)
			{
				nt[p].lmx=Max(nt[p].lmx,dl);nt[p].rmx=Max(nt[p].rmx,dr);
				Tmp=&nt[p].T;
				return p;
			}
			if(x<mid) nt[p].lc=tedit(nt[p].lc,tl,mid);
			else      nt[p].rc=tedit(nt[p].rc,mid,tr);
			renew=true;up(p);return p;
		}

		Sub::tree *edit(ll X,ll DL,ll DR)
		{
			x=X;dl=DL;dr=DR;
			rt=tedit(rt,0,maxn);
			return Tmp;
		}

		int del(int p,int tl,int tr)
		{
			if(!p || L<=tl) return 0;
			if(L<mid) nt[p].lc=del(nt[p].lc,tl,mid);
			nt[p].rc=del(nt[p].rc,mid,tr);
			renew=false;up(p);return p;
		}

		int merge(int x,int y,int tl,int tr)
		{
			if(!x) return y;if(!y) return x;//cerr<<"RUN";
			if(tl==tr-1)
			{
				nt[x].lmx=Max(nt[x].lmx,nt[y].lmx);
				nt[x].rmx=Max(nt[x].rmx,nt[y].rmx);
				nt[x].T.mrg(nt[y].T,ndis);
				return x;
			}

			fans=Max(fans,nt[nt[x].lc].lmx+nt[nt[y].rc].rmx-ndis);
			fans=Max(fans,nt[nt[y].lc].lmx+nt[nt[x].rc].rmx-ndis);

			nt[x].lc=merge(nt[x].lc,nt[y].lc,tl,mid);
			nt[x].rc=merge(nt[x].rc,nt[y].rc,mid,tr);
			renew=false;up(x);return x;
		}

		void mrg(tree &s){rt=merge(rt,s.rt,0,maxn);}
	};
}

Main::tree T[maxn];

inline void addedge(int l,int u,int v)
{
	e[en].v=v;e[en].l=l;e[en].n=head[u];head[u]=en++;
	e[en].v=u;e[en].l=l;e[en].n=head[v];head[v]=en++;
}
inline int lca(int u,int v)
{
	int i;if(dep[u]<dep[v]) swap(u,v);
	for(i=31-clz(dep[u]-dep[v]);~i;i--) if((dep[u]-dep[v])>>i&1) u=fa[u][i];
	if(u==v) return u;
	for(i=31-clz(dep[u]);~i;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}

void input_tree()
{
	int i;

	n=read();
	en=elrn=1;memset(head,0,sizeof(int)*(n+1));memset(fa,0,sizeof(int)*20*(n+1));
	for(i=1;i<=n;i++) elr[i].clear();
	for(i=1;i<n;i++) addedge(read(),read(),read());
}

void dfs(int x)
{
	int i;

	R[elrn]=dis[x];elr[x].pbk(elrn++);//printf("%d ",dis[x]);
	for(i=1;fa[x][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(i=head[x];i;i=e[i].n) if(e[i].v!=fa[x][0])
	{
		dep[e[i].v]=dep[x]+1;dis[e[i].v]=dis[x]+e[i].l;fa[e[i].v][0]=x;
		dfs(e[i].v);
		R[elrn]=dis[x];elr[x].pbk(elrn++);//printf("%d ",dis[x]);
	}
}

void input_way()
{
	int i;unsigned int j;
	int u,v,w;ll val;
	Sub::tree *Tmp;

	m=read();
	fans=-INFLL;Main::ntn=Sub::ntn=1;
	for(i=0;i<=n;i++) T[i].rt=0,T[i].ndis=dis[i];

	for(i=1;i<=m;i++)
	{
		w=lca(u=read(),v=read());val=dis[u]+dis[v]-2*dis[w]-read();

		if(u!=w){
			Tmp=T[u].edit(dep[w]+1,val,val+dis[w]);Tmp->wdis=dis[w];
			for(j=0;j<elr[v].size();j++) Tmp->edit(elr[v][j],val,i,dis[u]);
		}

		if(v!=w){
			Tmp=T[v].edit(dep[w]+1,val,val+dis[w]);Tmp->wdis=dis[w];
			for(j=0;j<elr[u].size();j++) Tmp->edit(elr[u][j],val,i,dis[v]);
		}
	//cerr<
	}
}

void solve(int x)
{
	int i;

	for(i=head[x];i;i=e[i].n) if(e[i].v!=fa[x][0])
	{
		solve(e[i].v);
		Main::L=dep[x]+1;T[e[i].v].del(T[e[i].v].rt,0,maxn);
		T[x].mrg(T[e[i].v]);
	}//printf("At x=%d  fans=%lld\n",x,fans);
}

int main()
{
	freopen("center.in","r",stdin);
	freopen("center.out","w",stdout);

	Sub::nt[0].init();Main::nt[0].init();
	int tt=read();
	while(tt--)
	{
		input_tree();
		dfs(1);
		R.init();
		input_way();
		solve(1);
		//write(tt);putchar(' ');
		if(fans<=-1e17) puts("F");
		else            write(fans),putchar('\n');
	}
	//cerr<

	return 0;
}

【总结】
膜拜出题组。
CSDN的博客真的是越改越菜了,这个代码片颜色怎么换啊,怎么复制还去掉了换行符啊。

你可能感兴趣的:(数据结构-线段树,Tree-LCA相关,其他-虚树)