zoukankan      html  css  js  c++  java
  • UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并

    原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html

    前言

    老年选手没有码力。

    题解

    先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$,点 x 在原树上的深度为 $d[x]$,那么

    $$d[x]+d[y] - d[LCA(x,y)] - d'[LCA(x,y)] = frac 12(D[x] + d[x]) + frac 12 (D[y] + d[y]) - d'[LCA(x,y)]$$

    于是我们考虑将分治区域内的节点在第二棵树上建虚树,并 DFS,每次维护一下子树中的 max(D[x] + d[x]) ,合并到父亲时顺便算一下答案。

    类似于WC2018通道,这样做的时间复杂度是可以强行优化成 $O(nlog n)$ 的。

    但是本题有更巧妙的做法。

    考虑边分树这个数据结构。它具有几个性质:

    1. 深度为 $O(log n)$,准确地说是略大于 $log _2 n $ 的,约等于 $log_{1.5} n$。(嗯对,xza深度只开了20,被我hack了kel)

    2. 叶子节点个数为 $n$ 。

    如果任取一种 DFS 序,并将其叶子按顺序排列,那么,两组节点的边分树合并的过程就可以看做以叶子 DFS 序为定义域的线段树合并。时间复杂度证明和线段树合并相同。写法也几乎相同。

    于是,我们得到下面的优秀算法:

    首先对第一棵树进行边分治,建出边分树。

    然后对第二棵树进行 DFS,用“边分树合并”来支持子树合并操作。在边分树合并的同时计算答案。 我们要维护的值仅仅是边分时两半集合中节点的权值 max 。

    时间复杂度 $O(nlog n)$ 。

    代码

    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof (x))
    #define For(i,a,b) for (int i=a;i<=b;i++)
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0,f=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		f|=ch=='-',ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?-x:x;
    }
    const int N=366677*2,S=N*13;
    const LL LLINF=1e18;
    struct Graph{
    	static const int M=N*2;
    	int cnt,y[M],fst[N],nxt[M],z[M];
    	void clear(){
    		cnt=1,clr(fst);
    	}
    	void add(int a,int b,int c){
    		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt,z[cnt]=c;
    	}
    	void Add(int a,int b,int c){
    		add(a,b,c),add(b,a,c);
    	}
    }t1,t2,T1;
    #define Forg(g) for (int E=g.fst[x],y=g.y[E];E;E=g.nxt[E],y=g.y[E]) if (y!=pre)
    int n,vcnt,ec;
    int td[N],vis[N],pos[N];
    LL d[N],val[27][N];
    void GetT1(int x,int pre,LL D){
    	int t=x,v;
    	d[x]=D;
    	Forg(t1)
    		d[v=++vcnt]=D,T1.Add(t,v,0),T1.Add(v,y,t1.z[E]),t=v,GetT1(y,x,D+t1.z[E]);
    }
    namespace DT{
    	int size[N],Mx[N],nowsize,kx,ky,kz;
    	void GetRT(int x,int pre,int flen){
    		size[x]=1;
    		Forg(T1)
    			if (!vis[y])
    				GetRT(y,x,T1.z[E]),size[x]+=size[y];
    		Mx[x]=max(size[x],nowsize-size[x]);
    		if (!kx||Mx[x]<Mx[kx])
    			kx=x,ky=pre,kz=flen;
    	}
    	void dfs(int x,int pre,LL D,int w){
    		pos[x]|=w<<td[x],val[td[x]++][x]=D+d[x];
    		Forg(T1)
    			if (!vis[y])
    				dfs(y,x,D+T1.z[E],w);
    	}
    	void Divide(int x,int Size){
    		if (Size>1){
    			kx=0,nowsize=Size,GetRT(x,0,0),x=kx;
    			int y=ky,z=kz,tmp=size[x];
    			dfs(x,y,0,0),dfs(y,x,z,1);
    			vis[y]=1,Divide(x,tmp),vis[y]=0;
    			vis[x]=1,Divide(y,Size-tmp);
    		}
    	}
    }
    int rt[N],ls[S],rs[S];
    LL Lmx[S],Rmx[S],ans=-LLINF;
    int st[S],top,cnt=0;
    int NewNode(){
    	return top?st[top--]:++cnt;
    }
    void RecNode(int x){
    	ls[x]=rs[x]=Lmx[x]=Rmx[x]=0,st[++top]=x;
    }
    void Ins(int &rt,int x,int D){
    	if (D<td[x]){
    		rt=NewNode(),Lmx[rt]=Rmx[rt]=-LLINF;
    		if (pos[x]>>D&1)
    			Ins(rs[rt],x,D+1),Rmx[rt]=max(Rmx[rt],val[D][x]);
    		else
    			Ins(ls[rt],x,D+1),Lmx[rt]=max(Lmx[rt],val[D][x]);
    	}
    }
    int Merge(int x,int y,LL add){
    	if (!x||!y)
    		return x|y;
    	ans=max(ans,max(Lmx[x]+Rmx[y],Rmx[x]+Lmx[y])/2+add);
    	Lmx[x]=max(Lmx[x],Lmx[y]),Rmx[x]=max(Rmx[x],Rmx[y]);
    	ls[x]=Merge(ls[x],ls[y],add),rs[x]=Merge(rs[x],rs[y],add);
    	return RecNode(y),x;
    }
    void Solve(int x,int pre,LL D){
    	ans=max(ans,d[x]-D),Ins(rt[x],x,0);
    	Forg(t2)
    		Solve(y,x,D+t2.z[E]),rt[x]=Merge(rt[x],rt[y],-D);
    }
    int main(){
    	n=read(),t1.clear(),t2.clear(),T1.clear();
    	For(i,1,n-1){
    		int x=read(),y=read(),z=read();
    		t1.Add(x,y,z);
    	}
    	For(i,1,n-1){
    		int x=read(),y=read(),z=read();
    		t2.Add(x,y,z);
    	}
    	vcnt=ec=n,GetT1(1,0,0),DT::Divide(1,vcnt),Solve(1,0,0);
    	cout<<ans<<endl;
    	return 0;
    }
    

      

  • 相关阅读:
    WPF使用Mutex创建单实例程序失效
    招式百出的数组(js)
    js中,如何把一个混杂的字符串进行去重并按数字在左,字母在右组成的新字符串打印出来
    字符串对象(js)
    时间对象(js)
    数组迭代的5个小宝贝(js)
    线程的状态
    线程
    JAVA学习规划
    简单的反射实例
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/UOJ400.html
Copyright © 2011-2022 走看看