[WC2018]通道


luogu传送门

这是我写过最难写的之一,写到AC的总时间有8h。另外Racheal,byebye~嘿嘿

Description

\(n\)个点,给三棵树,问\(x\)\(y\)在三棵树上的路径权值和最大。

Solution

第一棵树上边分治,边权为\(w\),划分为点集S和T。令\(d1_i\)\(i\)在T1中到边的距离。
同时令\(d_2,d_3\)分别表示在第三棵树上到根的距离。
此时总值为:\(d1_x+d1_y+dist2(x,y)+dist3(x,y)+w\)
发现可以改为\((d1_x+d2_x)+(d1_y+d2_y)+dist3(x,y)-2*d2[lca(x,y)]+w\)
如果枚举T2上的lca就可以把后两项看成常数了,而前面可以看作路径两端\(x\),\(y\)点权分别为\(d1[]+d2[]\),再加上路径长度。
在第二棵树上建\(S \bigcup T\)的虚树(\(k\)个点,离线+基数排序可以做到\(O(k)\)
然后这是个套路问题了。
合并两个集合\(S1,S2\)得到的点集的最长(短)路径,的两端一定是\(S1\)的两端与\(S2\)的两端中的两个。
因此就在虚树上按深度从下往上枚举lca,然后T3中合并,并维护最长路径长及端点。
黑恶,三棵树,咕咕咕~

code

点击查看代码
#include
using namespace std;
typedef long long ll;
const int N=5e5+5;
int lg[N],n,col[N],rt,pos[N],cnt;
ll d2[N],d3[N],d12[N],ans;
struct near {int x,y;ll D;}f[N][3];
namespace T3 {		//except get dist(lca) but nothing
	int nxt[N],to[N],head[N],ecnt,dfn[N],mnd[N][21],In[N],Time,dep[N],C[N][21];
	ll len[N];
	void add_edge(int u,int v,ll w) {
		nxt[++ecnt]=head[u];to[ecnt]=v;len[ecnt]=w;head[u]=ecnt;
		nxt[++ecnt]=head[v];to[ecnt]=u;len[ecnt]=w;head[v]=ecnt;
	}
	void _pp(int u,int fa) {
		dfn[In[u]=++Time]=u;
		for(int i=head[u];i;i=nxt[i]) {
			int v=to[i];if(v==fa)continue;
			dep[v]=dep[u]+1;d3[v]=d3[u]+len[i];
			_pp(v,u);
			dfn[++Time]=u;
		}
	}
	void _ST() {
//		for(int i=1;i<=Time;i++) printf("t=%d: %d %d\n",i,dfn[i],dep[dfn[i]]);
		for(int i=1;i<=Time;i++) mnd[i][0]=dep[dfn[i]],C[i][0]=dfn[i];
		for(int j=1;j<=lg[Time];j++) {
			int s=1<>1));
				if(mnd[i][j-1]v)swap(u,v);
		int k=lg[v-u+1],_ss=v-(1<>1));
				if(mnd[i][j-1]v)swap(u,v);
		int k=lg[v-u+1],_ss=v-(1<In[c]) {add_edge(st[tp-1],st[tp]);tp--;}
				if(c!=st[tp-1]) {add_edge(c,st[tp]);st[tp]=c;}
				else {add_edge(c,st[tp--]);}
			}
			st[++tp]=x;
		}
		while(tp>1) {add_edge(st[tp-1],st[tp]);tp--;}
	}
	ll Mx;
	ll Dis(int u,int v) {if(!u||!v)return 0;return d12[u]+d12[v]+T3::_dist(u,v);}
	void _merge(near &u,near v) {
//		printf("ST (%d,%d)  (%d,%d)\n",u.x,u.y,v.x,v.y); 
		if(!u.x) {u=v;return;}
		ll w1=u.D,w2=Dis(u.x,v.y),w3=Dis(u.y,v.x),w4=v.D,w5=Dis(u.x,v.x),w6=Dis(u.y,v.y);
		ll mx=max(max(max(w1,w2),max(w3,w4)),max(w5,w6));
		u.D=mx;
//		printf("!mx=%lld\n",mx);
		if(!mx||mx==w1) {return;}
		else if(mx==w2) {u.y=v.y;}
		else if(mx==w3) {u.x=v.x;}
		else if(mx==w4) {u=v;}
		else if(mx==w5) {u.y=v.x;}
		else {u.x=v.y;}
		if(!u.x) u.x=u.y,u.y=0;
//		printf("Ed (%d,%d,%lld) \n",u.x,u.y,u.D); 
	}
	ll Mx_v(near u,near v) {
//		printf("(%d,%d)  (%d,%d)\n",u.x,u.y,v.x,v.y); 
		return max(max(Dis(u.x,v.x),Dis(u.x,v.y)),max(Dis(u.y,v.x),Dis(u.y,v.y)));
	}
	void dfs(int u) {
		if(col[u]==1) f[u][1]=(near){u,0,0},f[u][2]=(near){0,0,0};
		else f[u][1]=(near){0,0,0},f[u][2]=(near){u,0,0};
		for(int i=head[u];i;i=nxt[i]) {
			int v=to[i];
			dfs(v);
//			printf("(%d,%d) %lld\n",u,v,Mx);
			Mx=max(Mx,max(Mx_v(f[u][2],f[v][1]),Mx_v(f[u][1],f[v][2]))-2*d2[u]);
			if(f[v][1].x)_merge(f[u][1],f[v][1]);
			if(f[v][2].x)_merge(f[u][2],f[v][2]);
//			printf("x1=%d x2=%d\n",f[u][1].x,f[u][2].x);
		}
	}
	ll solve() {
		Build_T();
		Mx=0;dfs(1);
		ecnt=0;head[1]=0;for(int i=1;i<=cnt;i++)head[pos[i]]=0;
		return Mx;
	}
}

namespace T1 {
	int nxt[N],to[N],head[N],ecnt=1,Nxt[N],To[N],Head[N],Ecnt,nd,we,sz[N],c;
	bool vis[N];
	ll len[N],Len[N];
	void Add_edge(int u,int v,ll w) {
		Nxt[++Ecnt]=Head[u];To[Ecnt]=v;Len[Ecnt]=w;Head[u]=Ecnt;
		Nxt[++Ecnt]=Head[v];To[Ecnt]=u;Len[Ecnt]=w;Head[v]=Ecnt;
	}
	void add_edge(int u,int v,ll w) {
//		printf("!%d %d %lld\n",u,v,w);
		nxt[++ecnt]=head[u];to[ecnt]=v;len[ecnt]=w;head[u]=ecnt;
		nxt[++ecnt]=head[v];to[ecnt]=u;len[ecnt]=w;head[v]=ecnt;
	}
	void _to2(int u,int fa) {
		int lst=0;
		for(int i=Head[u];i;i=Nxt[i]) {
			int v=To[i];if(v==fa)continue;
			_to2(v,u);
			if(!lst) add_edge(u,v,Len[i]),lst=u;
			else {++nd;add_edge(lst,nd,0);add_edge(nd,v,Len[i]);lst=nd;}
		}
	}
	void init() {
		lg[1]=0;for(int i=2;i<=(n<<1);i++) lg[i]=lg[i>>1]+1;
		for(int i=1;i