zoukankan      html  css  js  c++  java
  • [LOJ#2553][CTSC2018]暴力写挂

    [LOJ#2553][CTSC2018]暴力写挂

    试题描述

    temporaryDO 是一个很菜的 OIer 。在 4 月,他在省队选拔赛的考场上见到了《林克卡特树》一题,其中 (k = 0) 的部分分是求树 (T) 上的最长链。可怜的 temporaryDO 并不会做这道题,他在考场上抓猫耳挠猫腮都想不出一点思路。

    这时,善良的板板出现在了空中,他的身上发出璀璨却柔和的光芒,荡漾在考场上。‘‘题目并不难。’’ 板板说。那充满磁性的声音,让 temporaryDO 全身充满了力量。 他决定:写一个枚举点对求 LCA 算距离的 (k = 0) 的 $$O(n^2log n)$ 的部分分程序!于是, temporaryDO 选择以 (1) 为根,建立了求 LCA 的树链剖分结构,然后写了二重 for 循环枚举点对。

    然而,菜菜的 temporaryDO 不小心开小了数组,于是数组越界到了一片神秘的内存区域。但恰好的是,那片内存区域存储的区域恰好是另一棵树 (T')。这样一来,程序并没有 RE ,但他求 (x)(y) 的距离的时候,计算的是

    [depth(x) + depth(y) - (depth(LCA(x, y)) + depth'(LCA'(x, y))) ]

    最后程序会输出每一对点对 (i, j (i le j)) 的如上定义的‘‘距离’’ 的最大值。

    temporaryDO 的程序在评测时光荣地爆零了。但他并不服气,他决定花好几天把自己的程序跑出来。请你根据 (T)(T') 帮帮可怜的 temporaryDO 求出他程序的输出。

    输入

    第一行包含一个整数 (n) ,表示树上的节点个数;

    (2) 到第 (n) 行,每行三个整数 (x , y , v),表示 (T) 中存在一条从 (x)(y) 的边,其长度为 (v); 第 (n + 1) 到第 (2n - 1) 行,每行三个整数 (x , y , v),表示 (T') 中存在一条从 (x)(y) 的边,其长度为 (v)

    输出

    输出一行一个整数,表示 temporaryDO 的程序的输出。

    输入示例

    6
    1 2 2
    1 3 0
    2 4 1
    2 5 -7
    3 6 0
    1 2 -1
    2 3 -1
    2 5 3
    2 6 -2
    3 4 8
    

    输出示例

    5
    

    数据规模及约定

    对于所有数据, (n le 366666 , |v| le 2017011328)

    (depth(p))(depth'(p)) 分别表示树 (T)(T') 中点 (1) 到点 (p) 的距离,这里规定,距离指的是经过的边的边权总和,其中 (depth(1) = 0)

    (LCA(x, y))(LCA'(x, y)) 分别表示树 (T)(T') 中点 (x) 与点 (y) 的最近公共祖先,即在从 (x)(y) 的最短路径上的距离根经过边数最少的点。

    题解

    搁置已久的大锅终于搞掉了……这个题让自带大常数的我卡得生活不能自理……

    将第一棵树转化成二叉树后边分治,那么考虑重心边的两侧一定有一侧是更“靠近”根的;假设集合 (A) 离根更近,集合 (B) 离根更远,那么 (forall x in A) 都有 (forall y in B, y' in B, lca(x, y) = lca(x, y')),其中 (lca(a, b)) 表示在原树上 (a)(b) 的最近公共祖先。

    那么这样我们可以枚举 (x),然后 (depth(x) - depth(lca(x, y))) 就固定了,我们就是要找到最小的 (depth(y) - depth'(lca'(x, y))),这个东西建一下虚树然后树形 dp 即可(正反两次 dp,一次从子树往上推,第二次从父节点更新到子节点)。

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
    #define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
    
    const int BufferSize = 1 << 16;
    char buffer[BufferSize], *Head, *Tail;
    char Getchar() {
    	if(Head == Tail) {
    		int l = fread(buffer, 1, BufferSize, stdin);
    		Tail = (Head = buffer) + l;
    	}
    	return *Head++;
    }
    int read() {
    	int x = 0, f = 1; char c = Getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    	return x * f;
    }
    
    #define maxn 800010
    #define maxm 1600010
    #define maxlog 21
    #define pii pair <int, int>
    #define x first
    #define y second
    #define mp(x, y) make_pair(x, y)
    #define LL long long
    
    int Log[maxn<<1];
    struct tree {
    	int n, m, head[maxn], nxt[maxm], to[maxm], dist[maxm];
    	
    	tree(): m(0) { memset(head, 0, sizeof(head)); }
    	
    	void AddEdge(int a, int b, int c) {
    		to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
    		swap(a, b);
    		to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
    		return ;
    	}
    } Tmp;
    struct Tree {
    	int n, m, head[maxn], nxt[maxm], to[maxm], dist[maxm], id[maxm], dep[maxn], dfn[maxn], clo, mnp[maxlog][maxn<<1];
    	LL Dep[maxn];
    	
    	Tree(): m(0) { memset(head, 0, sizeof(head)); }
    	
    	void AddEdge(int a, int b, int c, int Id = 0) {
    		to[++m] = b; dist[m] = c; id[m] = Id; nxt[m] = head[a]; head[a] = m;
    		swap(a, b);
    		to[++m] = b; dist[m] = c; id[m] = Id; nxt[m] = head[a]; head[a] = m;
    		return ;
    	}
    	
    	void build(int u, int fa) {
    		mnp[0][dfn[u] = ++clo] = u;
    		for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
    			dep[to[e]] = dep[u] + 1;
    			Dep[to[e]] = Dep[u] + dist[e];
    			build(to[e], u);
    			mnp[0][++clo] = u;
    		}
    		return ;
    	}
    	void rmq_init() {
    		Log[1] = 0;
    		rep(i, 2, clo) Log[i] = Log[i>>1] + 1;
    		rep(i, 1, Log[clo])
    			rep(j, 1, clo - (1 << i) + 1) {
    				int a = mnp[i-1][j], b = mnp[i-1][j+(1<<i>>1)];
    				mnp[i][j] = dep[a] < dep[b] ? a : b;
    			}
    		return ;
    	}
    	int lca(int a, int b) {
    		int l = dfn[a], r = dfn[b];
    		if(l > r) swap(l, r);
    		int t = Log[r-l+1], A = mnp[t][l], B = mnp[t][r-(1<<t)+1];
    		return dep[A] < dep[B] ? A : B;
    	}
    } T, T1;
    
    namespace rebuildTree {
    	int sons[maxn], sonv[maxn], cs, M;
    	void getT(int u, int fa) {
    		for(int e = Tmp.head[u]; e; e = Tmp.nxt[e]) if(Tmp.to[e] != fa) getT(Tmp.to[e], u);
    		cs = 0;
    		for(int e = Tmp.head[u]; e; e = Tmp.nxt[e]) if(Tmp.to[e] != fa) sons[++cs] = Tmp.to[e], sonv[cs] = Tmp.dist[e];
    		// printf("%d has %d sons
    ", u, cs);
    		if(cs <= 2) rep(i, 1, cs) T.AddEdge(u, sons[i], sonv[i], ++M);
    		else {
    			T.n++; T.AddEdge(T.n, sons[1], sonv[1], ++M); T.AddEdge(T.n, sons[2], sonv[2], ++M);
    			rep(i, 3, cs - 1) T.n++, T.AddEdge(T.n, T.n - 1, 0, ++M), T.AddEdge(T.n, sons[i], sonv[i], ++M);
    			T.AddEdge(u, T.n, 0, ++M); T.AddEdge(u, sons[cs], sonv[cs], ++M);
    		}
    		return ;
    	}
    	
    	void build() {
    		getT(1, 0);
    		// printf("M: %d, %d
    ", M, T.n);
    		T.build(1, 0); T1.build(1, 0);
    		T.rmq_init(); T1.rmq_init();
    		return ;
    	}
    }
    
    namespace Solve {
    	const LL ool = 1ll << 60;
    	
    	LL ans = -ool;
    	
    	namespace Vtree {
    		int KeyPoint[maxn], K;
    		int m, head[maxn], nxt[maxm], to[maxm];
    		LL val[maxn], extra[maxn];
    		
    		void clear() {
    			rep(i, 1, K) val[KeyPoint[i]] = extra[KeyPoint[i]] = -ool, head[KeyPoint[i]] = 0;
    			K = m = 0;
    			return ;
    		}
    		
    		void AddEdge(int a, int b) {
    			to[++m] = b; nxt[m] = head[a]; head[a] = m;
    			return ;
    		}
    		
    		LL f[maxn], pre[maxn], suf[maxn], sonv[maxn];
    		int son[maxn], cs;
    		void dp(int u, int fa) {
    			f[u] = val[u];
    			for(int e = head[u]; e; e = nxt[e]) dp(to[e], u), f[u] = max(f[u], f[to[e]]);
    			cs = 0;
    			for(int e = head[u]; e; e = nxt[e]) son[++cs] = to[e], sonv[cs] = f[to[e]];
    			LL nmx = -ool;
    			rep(i, 1, cs) {
    				pre[son[i]] = nmx;
    				nmx = max(nmx, sonv[i]);
    			}
    			nmx = -ool;
    			dwn(i, cs, 1) {
    				suf[son[i]] = nmx;
    				nmx = max(nmx, sonv[i]);
    			}
    			return ;
    		}
    		void dp2(int u, int fa, LL nmx) {
    			if(extra[u] > -ool && (f[u] > -ool || nmx > -ool)) ans = max(ans, extra[u] + max(f[u] - T1.Dep[u], nmx));
    			for(int e = head[u]; e; e = nxt[e]) {
    				LL now = max(max(pre[to[e]], suf[to[e]]), val[u]);
    				dp2(to[e], u, max(nmx, now > -ool ? now - T1.Dep[u] : -ool));
    			}
    			return ;
    		}
    		
    		bool cmp(const int &a, const int &b) { return T1.dfn[a] < T1.dfn[b]; }
    		void build() {
    			sort(KeyPoint + 1, KeyPoint + K + 1, cmp);
    			rep(i, 1, K - 1) KeyPoint[++K] = T1.lca(KeyPoint[i], KeyPoint[i+1]);
    			KeyPoint[++K] = T1.lca(KeyPoint[K], KeyPoint[1]);
    			sort(KeyPoint + 1, KeyPoint + K + 1, cmp);
    			K = unique(KeyPoint + 1, KeyPoint + K + 1) - KeyPoint - 1;
    			rep(i, 1, K - 1) {
    				int a = KeyPoint[i], b = KeyPoint[i+1], c = T1.lca(a, b);
    				AddEdge(c, b);
    			}
    			dp(KeyPoint[1], 0); dp2(KeyPoint[1], 0, -ool);
    			return ;
    		}
    	}
    	using Vtree::KeyPoint;
    	using Vtree::K;
    	using Vtree::val;
    	using Vtree::extra;
    	
    	pii rt;
    	int eid, size, best, siz[maxn];
    	bool vis[maxn];
    	
    	void getrt(int u, int fa, int fae) {
    		siz[u] = 1;
    		for(int e = T.head[u]; e; e = T.nxt[e]) if(T.id[e] != fae && !vis[T.id[e]]) {
    			int v = T.to[e];
    			getrt(v, u, T.id[e]);
    			siz[u] += siz[v];
    		}
    		if(fa && best > max(siz[u], size - siz[u])) best = max(siz[u], size - siz[u]), rt = mp(u, fa), eid = fae;
    		return ;
    	}
    	
    	void dfs(int u, int fae, bool tp) {
    		if(u <= T1.n) {
    			KeyPoint[++K] = u;
    			if(tp) val[u] = T.Dep[u];
    			else extra[u] = T.Dep[u] - T.Dep[T.lca(u,rt.x)];
    		}
    		for(int e = T.head[u]; e; e = T.nxt[e]) if(T.id[e] != fae && !vis[T.id[e]]) dfs(T.to[e], T.id[e], tp);
    		return ;
    	}
    	void solve(pii u, int ed) {
    		// printf("%d(%d) -- %d(%d)
    ", u.x, siz[u.x], u.y, siz[u.y]);
    		vis[ed] = 1;
    		if(T.dep[u.x] > T.dep[u.y]) swap(u.x, u.y);
    		dfs(u.x, 0, 0); dfs(u.y, 0, 1);
    		Vtree::build();
    		Vtree::clear();
    		
    		if(siz[u.x] > 1) {
    			rt = mp(0, 0); eid = 0; best = (size = siz[u.x]) + 1; getrt(u.x, 0, 0);
    			if(eid) solve(rt, eid);
    		}
    		if(siz[u.y] > 1) {
    			rt = mp(0, 0); eid = 0; best = (size = siz[u.y]) + 1; getrt(u.y, 0, 0);
    			if(eid) solve(rt, eid);
    		}
    		return ;
    	}
    	
    	void main() {
    		rep(i, 1, T1.n) val[i] = extra[i] = -ool;
    		rt = mp(0, 0); eid = 0; best = (size = T.n) + 1; getrt(1, 0, 0);
    		solve(rt, eid);
    		rep(i, 1, T1.n) ans = max(ans, T.Dep[i] - T1.Dep[i]);
    		return ;
    	}
    }
    
    int main() {
    	Tmp.n = T.n = T1.n = read();
    	rep(i, 1, Tmp.n - 1) {
    		int a = read(), b = read(), c = read();
    		Tmp.AddEdge(a, b, c);
    	}
    	rep(i, 1, T1.n - 1) {
    		int a = read(), b = read(), c = read();
    		T1.AddEdge(a, b, c);
    	} // */
    	
    	rebuildTree::build();
    	Solve::main();
    	
    	printf("%lld
    ", Solve::ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    ipv6现状,加英文的中括号访问, ipv6测试http://test-ipv6.com
    从30岁至35岁:为你的生命多积累一些厚度
    delete
    国内投行的工资
    南京大学行业求职研究报告——投资银行篇
    【oracle案件】ORA-19502,ORA-27072
    多线程第四篇秒杀 一个经典的多线程同步问题
    霍夫的图像处理变换(线检测算法)
    hadoop-ha组态
    央视公然诽谤Linux操作系统,谁报告?
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/9085258.html
Copyright © 2011-2022 走看看