• 题解 「CTSC2018暴力写挂」


    题目传送门

    题目大意

    给出两个大小为 (n) 的树,求出:

    [max{ ext{depth}(x)+ ext{depth}(y)- ext{depth}( ext{LCA}(x,y)- ext{depth}^{'}( ext{LCA}^{'}(x,y)))} ]

    (nle 3666666),答案保证在 ( ext{long long}) 范围内。

    思路

    边分治秒啊,终于学会了 边分树合并 了,在这里记录一下,以免后面忘掉了。

    首先我们可(bu)以(ke)想(neng)到(de)一种 (Theta(nlog^2 n)) 的做法,就是说我们可以先把式子化成:

    [frac{1}{2}( ext{dist}(x,y)+ ext{depth}(x)+ ext{depth}(y)-2 ext{depth}^{'}( ext{LCA}^{'}(x,y))) ]

    然后你发现如果不看最后那个东西的话,前面那个可以用边分治解决。然后我们发现我们可以对于第二棵树建出当前分治的点集的虚树,然后枚举某一个点作为 ( ext{LCA}^{'}(x,y)) 。然后你就发现时间复杂度是 (Theta(nlog^2 n)) ,而且常数巨大,无法通过此题。

    然后我们需要引入一个叫「边分树」的东西,它跟点分树完全不一样。对于一个点我们维护一个它每一次被分治到的贡献,这道题目当中即为 ( ext{dis}(x)+ ext{depth}(x))( ext{dis}(x)) 即为 (x) 到分治点的距离),但是光是这样还不够,我们还需要能够更新答案,也就是说我们需要知道每次分治所属的块,这就是为什么需要是二叉树链的原因,如果某个点是它父亲的左节点,就说明他父亲是左边的块(左右其实是自己定义的,但这并不是很重要)。

    考虑合并两个二叉树,对于同一相同路径走到的 (x,y) ,显然它们可以合成一个合法答案,直接连起来即可。

    考虑如何弄到第二棵树上去,你发现如果钦定某一个点为 ( ext{LCA}),那么就可以用已有的点与子子树进行合并更新答案即可,不过我们还需要一个点对它自己的贡献。

    时间复杂度是 (Theta(nlog n)),但是常数比较大。

    ( exttt{Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define INF 0x7f7f7f7f7f
    #define ll long long
    #define MAXN 400250
    
    char buf[1 << 25],*p1 = buf,*p2 = buf;
    #define getchar() ((p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 25,stdin))) ? EOF : *p1 ++)
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    #define ls(x) tree[x].ls
    #define rs(x) tree[x].rs
    #define vl(x) tree[x].vl
    #define vr(x) tree[x].vr
    
    struct node{
    	ll vl,vr;
    	int ls,rs;
    	node(){vl = vr = -INF;}
    }tree[MAXN * 25];
    
    int n,cnt,root[MAXN],las[MAXN];
    
    namespace T1{
    #define N 800250
    	ll dis[N],wei[N << 1];
    	int lim,ed,tot,toop = 1,to[N << 1],nxt[N << 1],vis[N << 1],siz[N],head[N];
    	void Add_Edge (int u,int v,ll w){
    		to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
    		to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
    	}
    	void getdis (int u,int fa){for (Int i = head[u];i;i = nxt[i]) if (to[i] ^ fa) dis[to[i]] = dis[u] + wei[i],getdis (to[i],u);}
    	void findedge (int u,int fa,int Siz){
    		siz[u] = 1;
    		for (Int i = head[u];i;i = nxt[i]){
    			int v = to[i];
    			if (v == fa || vis[i]) continue;
    			findedge (v,u,Siz),siz[u] += siz[v];
    			int tmp = max (siz[v],Siz - siz[v]);
    			if (tmp < lim) lim = tmp,ed = i;
    		}
    	}
    	void dfs (int u,int fa,ll Dis,bool kase){
    		if (u <= n){
    			++ tot;
    			if (ls(las[u]) == -1) ls(las[u]) = tot;
    			else rs(las[u]) = tot;
    			if (kase == 0) ls(tot) = -1,vl(tot) = Dis + dis[u];
    			else rs(tot) = -1,vr(tot) = Dis + dis[u];
    			las[u] = tot;
    		}
    		for (Int i = head[u];i;i = nxt[i]){
    			int v = to[i];if (v == fa || vis[i]) continue;
    			dfs (v,u,Dis + wei[i],kase);
    		}
    	}
    	void divide (int u,int Siz){
    		if (Siz <= 1) return ;
    		lim = Siz,findedge (u,0,Siz),vis[ed] = vis[ed ^ 1] = 1;
    		int tx = to[ed],ty = to[ed ^ 1];
    		dfs (tx,0,0,0),dfs (ty,0,wei[ed],1);
    		divide (ty,Siz - siz[tx]),divide (tx,siz[tx]);
    	}
    	void Work (){
    		for (Int i = 1;i <= n;++ i) root[i] = las[i] = ++ tot,ls(root[i]) = -1;
    		getdis (1,0),divide (1,cnt);
    	}
    #undef N
    }
    
    namespace T2{
    	ll ans,now,wei[MAXN << 1];
    	int toop = 1,to[MAXN << 1],nxt[MAXN << 1],head[MAXN];
    	void Add_Edge (int u,int v,ll w){
    		to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
    		to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
    	}
    	int Merge (int x,int y){
    		if (!x || !y) return x + y;
    		ans = max (ans,max (vl(x) + vr(y),vr(x) + vl(y)) - now);
    		ls(x) = Merge (ls(x),ls(y)),rs(x) = Merge (rs(x),rs(y));
    		vl(x) = max (vl(x),vl(y)),vr(x) = max (vr(x),vr(y));
    		return x;
    	}
    	void dfs (int u,int fa,ll dis){
    		for (Int i = head[u];i;i = nxt[i]){
    			int v = to[i];if (v == fa) continue;
    			dfs (v,u,dis + wei[i]),now = dis * 2,root[u] = Merge (root[u],root[v]); 
    		} 
    		ans = max (ans,(T1::dis[u] - dis) * 2);
    	}
    	void Work (){
    		dfs (1,0,0),write (ans / 2),putchar ('
    ');
    	}
    }
    
    #define N 800250
    
    ll wei[N << 1];
    int toop = 1,to[N << 1],nxt[N << 1],head[N];
    
    void Add_Edge (int u,int v,ll w){
    	to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;
    	to[++ toop] = u,wei[toop] = w,nxt[toop] = head[v],head[v] = toop;
    }
    
    void dfs (int u,int fa){
    	for (Int i = head[u];i;i = nxt[i]){
    		int v = to[i];ll w = wei[i];
    		if (v == fa) continue;
    		dfs (v,u);
    		if (!las[u]) T1::Add_Edge (u,v,w),las[u] = u;
    		else T1::Add_Edge (las[u],++ cnt,0),T1::Add_Edge (las[u] = cnt,v,w);
    	}
    }
    
    signed main(){
    	read (n),cnt = n;
    	for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),Add_Edge (u,v,w);
    	for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),T2::Add_Edge (u,v,w);
    	dfs (1,0),T1::Work ();T2::Work ();
    	return 0;
    }
    
  • 相关阅读:
    点击按钮,回到页面顶部的5种写法
    node知识积累
    Python3基础 str ljust-rjust-center 左、右对齐 居中
    Python3基础 str : 对字符串进行切片
    Python3基础 str : 字符串的逆序
    Python3基础 str __add__ 拼接,原字符串不变
    Python3基础 运算 加减乘除、取余数
    Python3基础 只有int类型,没有long类型
    Python3基础 complex 声明复数
    Python3基础 complex real imag __abs__ 取复数的实部 虚部 模
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13602966.html
Copyright © 2020-2023  润新知