zoukankan      html  css  js  c++  java
  • UOJ#347. 【WC2018】通道(边分治)

    传送门
    就是求两个点 (a,b) 使得 (dis_1(a,b)+dis_2(a,b)+dis_3(a,b)) 最大

    step1

    对第一棵树边分治
    那么变成 (d_1(a)+d_1(b)+dis_2(a,b)+dis_3(a,b)) 最大
    并且 (a,b) 属于边分开的不同的集合 (S,T)

    边分治

    对于一条边,算经过这条边的路径的答案
    点分治不方便的就是同一棵子树的容斥,而边分治不用考虑
    直接边分治显然菊花就卡掉了
    所以我们要转二叉树
    具体来说就是把一个点的儿子建一棵线段树,添加虚拟节点,点权为其父亲的点权,只有线段树的叶子节点有边权

    step2

    对第二棵树建虚树
    建出 (S|T) 的虚树
    变成 (d_1(a)+d_2(a)+d_1(b)+d_2(b)+dis_3(a,b)-2d_2(lca_2(a,b)))
    (ain S,bin T)
    枚举 (lca) 那么变成求一个类似于 (w_1(a)+w_2(b)+dis_3(a,b)) 的最大值

    step3

    第三棵树不需要什么,上式类似于一个求最长链的东西
    而两棵子树合并之后的最长链的端点一定是原来两棵树的最长链的端点
    所以只需要在第三棵树上求距离就好了,然后在第二棵树上 (dp) 维护

    奉上 (7.6kb) 大常数代码

    # include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int maxn(1e5 + 5);
    
    int cnt1, cnt2, lef[maxn], rig[maxn];
    ll ans, val[maxn];
    
    namespace Tree1 {
    	int n, first[maxn], cnt, deep[maxn], st[20][maxn << 1], lg[maxn << 1], dfn[maxn], idx;
    	ll dis[maxn];
    
    	struct Edge {
    		int to, next;
    		ll w;
    	} edge[maxn << 1];
    
    	inline void Add(int u, int v, ll w) {
    		edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
    		edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
    	}
    
    	inline int Min(int x, int y) {
    		return deep[x] < deep[y] ? x : y;
    	}
    
    	void Dfs(int u, int ff) {
    		int e, v;
    		dfn[u] = ++idx, st[0][idx] = u;
    		for (e = first[u]; ~e; e = edge[e].next)
    			if ((v = edge[e].to) != ff) {
    				dis[v] = dis[u] + edge[e].w;
    				deep[v] = deep[u] + 1;
    				Dfs(v, u), st[0][++idx] = u;
    			}
    	}
    
    	inline int LCA(int u, int v) {
    		if (u == v) return u;
    		u = dfn[u], v = dfn[v];
    		if (u > v) swap(u, v);
    		int len;
    		len = lg[v - u + 1];
    		return Min(st[len][u], st[len][v - (1 << len) + 1]);
    	}
    
    	inline ll Dis(int u, int v) {
    		if (!u || !v) return -1e18;
    		int lca;
    		lca = LCA(u, v);
    		return val[u] + val[v] + dis[u] + dis[v] - (dis[lca] << 1);
    	}
    
    	void Init() {
    		int u, v, i, j;
    		ll w;
    		memset(first, -1, sizeof(first));
    		for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add(u, v, w);
    		Dfs(1, 0);
    		for (i = 2; i <= idx; ++i) lg[i] = lg[i >> 1] + 1;
    		for (j = 1; j <= lg[idx]; ++j)
    			for (i = 1; i + (1 << j) - 1 <= idx; ++i)
    				st[j][i] = Min(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
    	}
    }
    
    namespace Tree2 {
    	int n, first[maxn], cnt, deep[maxn], st[20][maxn << 1], lg[maxn << 1], dfn[maxn], idx, dfx[maxn], pid;
    	int head[maxn], que[maxn], len, size[maxn], sta[maxn], vis[maxn];
    	ll dis[maxn];
    
    	struct Edge {
    		int to, next;
    		ll w;
    	} edge[maxn << 1], edg[maxn << 1];
    
    	inline void Add1(int u, int v, ll w) {
    		edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
    		edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
    	}
    
    	inline void Add2(int u, int v) {
    		edg[cnt] = (Edge){v, head[u]}, head[u] = cnt++;
    	}
    
    	inline int Min(int x, int y) {
    		return deep[x] < deep[y] ? x : y;
    	}
    
    	void Dfs(int u, int ff) {
    		int e, v;
    		dfn[u] = ++idx, st[0][idx] = u, dfx[u] = ++pid, size[u] = 1;
    		for (e = first[u]; ~e; e = edge[e].next)
    			if ((v = edge[e].to) != ff) {
    				dis[v] = dis[u] + edge[e].w;
    				deep[v] = deep[u] + 1;
    				Dfs(v, u), st[0][++idx] = u;
    				size[u] += size[v];
    			}
    	}
    
    	inline int LCA(int u, int v) {
    		if (u == v) return u;
    		u = dfn[u], v = dfn[v];
    		if (u > v) swap(u, v);
    		int len;
    		len = lg[v - u + 1];
    		return Min(st[len][u], st[len][v - (1 << len) + 1]);
    	}
    
    	struct Info {
    		int u, v;
    		ll dis;
    
    		inline Info(int _u = 0, int _v = 0) {
    			u = _u, v = _v, dis = Tree1 :: Dis(u, v);
    		}
    
    		inline bool operator <(Info b) const {
    			return dis < b.dis;
    		}
    	} f1[maxn], f2[maxn];
    
    	inline Info operator +(Info a, Info b) {
    		return max(max(max(a, b), max(Info(a.u, b.u), Info(a.u, b.v))), max(Info(a.v, b.u), Info(a.v, b.v)));
    	}
    
    	inline Info Merge(Info a, Info b) {
    		return max(max(Info(a.u, b.u), Info(a.u, b.v)), max(Info(a.v, b.u), Info(a.v, b.v)));
    	}
    
    	void Init() {
    		int u, v, i, j;
    		ll w;
    		memset(first, -1, sizeof(first));
    		for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add1(u, v, w);
    		Dfs(1, 0);
    		for (i = 2; i <= idx; ++i) lg[i] = lg[i >> 1] + 1;
    		for (j = 1; j <= lg[idx]; ++j)
    			for (i = 1; i + (1 << j) - 1 <= idx; ++i)
    				st[j][i] = Min(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
    	}
    
    	inline int Cmp(int x, int y) {
    		return dfx[x] < dfx[y];
    	}
    
    	void Calc(int u, ll add) {
    		int e, v;
    		f1[u] = f2[u] = Info();
    		if (vis[u]) (vis[u] == 1) ? f1[u] = Info(u, u) : f2[u] = Info(u, u);
    		for (e = head[u]; ~e; e = edg[e].next) {
    			v = edg[e].to, Calc(v, add);
    			ans = max(ans, max(Merge(f1[u], f2[v]), Merge(f1[v], f2[u])).dis + add - (dis[u] << 1));
    			f1[u] = f1[u] + f1[v], f2[u] = f2[u] + f2[v];
    		}
    	}
    
    	void Solve(ll add) {
    		int i, t, tp;
    		len = cnt = tp = 0;
    		for (i = 1; i <= cnt1; ++i) que[++len] = lef[i], vis[lef[i]] = 1;
    		for (i = 1; i <= cnt2; ++i) que[++len] = rig[i], vis[rig[i]] = 2;
    		for (i = 1; i <= len; ++i) val[que[i]] += dis[que[i]];
    		sort(que + 1, que + len + 1, Cmp), t = len;
    		for (i = 1; i < t; ++i) que[++len] = LCA(que[i], que[i + 1]);
    		sort(que + 1, que + len + 1, Cmp);
    		len = unique(que + 1, que + len + 1) - que - 1;
    		for (i = 1; i <= len; ++i) head[que[i]] = -1;
    		for (i = 1; i <= len; ++i) {
    			while (tp && dfx[sta[tp]] + size[sta[tp]] <= dfx[que[i]]) --tp;
    			if (tp) Add2(sta[tp], que[i]);
    			sta[++tp] = que[i];
    		}
    		Calc(sta[1], add);
    		for (i = 1; i <= len; ++i) val[que[i]] = vis[que[i]] = 0;
     	}
    }
    
    namespace Tree3 {
    	int n, first[maxn << 2], cnt, head[maxn], tmp[maxn << 2], len, m;
    	int size[maxn << 2], vis[maxn << 2], rte, sz, mn, st1[maxn], tp1, st2[maxn], tp2;
    	ll dis[maxn << 2], fw[maxn << 2];
    
    	struct Edge {
    		int to, next;
    		ll w;
    	} edge[maxn * 8], edg[maxn << 1];
    
    	inline void Add1(int u, int v, ll w) {
    		edg[cnt] = (Edge){v, head[u], w}, head[u] = cnt++;
    		edg[cnt] = (Edge){u, head[v], w}, head[v] = cnt++;
    	}
    
    	inline void Add2(int u, int v, ll w) {
    		edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
    		edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
    	}
    
    	int Build(int l, int r) {
    		if (l > r) return 0;
    		if (l == r) return tmp[l];
    		int mid, ls, rs, cur;
    		mid = (l + r) >> 1, cur = ++n;
    		ls = Build(l, mid), rs = Build(mid + 1, r);
    		if (ls) Add2(cur, ls, ls <= m ? fw[ls] : 0);
    		if (rs) Add2(cur, rs, rs <= m ? fw[rs] : 0);
    		return cur;
    	}
    
    	void Rebuild(int u, int ff) {
    		int e, v, mid, ls, rs;
    		len = 0;
    		for (e = head[u]; ~e; e = edg[e].next)
    			if ((v = edg[e].to) != ff) fw[v] = edg[e].w, tmp[++len] = v;
    		mid = (len + 1) >> 1;
    		ls = Build(1, mid), rs = Build(mid + 1, len);
    		if (ls) Add2(u, ls, ls <= m ? fw[ls] : 0);
    		if (rs) Add2(u, rs, rs <= m ? fw[rs] : 0);
    		for (e = head[u]; ~e; e = edg[e].next)
    			if ((v = edg[e].to) != ff) Rebuild(v, u);
    	}
    
    	void Getroot(int u, int ff, int fe) {
    		int e, v;
    		size[u] = 1;
    		for (e = first[u]; ~e; e = edge[e].next)
    			if ((v = edge[e].to) != ff && !vis[e]) Getroot(v, u, e), size[u] += size[v];
    		if (abs(sz - size[u] - size[u]) < mn) rte = fe, mn = abs(sz - size[u] - size[u]);
    	}
    
    	void Dfs(int u, int ff, ll d) {
    		int e, v;
    		if (u <= m) st1[++tp1] = u;
    		dis[u] = d;
    		for (e = first[u]; ~e; e = edge[e].next)
    			if ((v = edge[e].to) != ff && !vis[e]) Dfs(v, u, d + edge[e].w);
    	}
    
    	void Solve(int e) {
    		if (e < 0) return;
    		int tmp1, tmp2, i, u1, u2;
    		u1 = edge[e].to, u2 = edge[e ^ 1].to;
    		vis[e] = vis[e ^ 1] = 1, tp1 = tp2 = 0;
    		Dfs(u1, u2, 0);
    		while (tp1) st2[++tp2] = st1[tp1], --tp1;
    		Dfs(u2, u1, 0);
    		cnt1 = cnt2 = 0;
    		for (i = 1; i <= tp1; ++i) val[st1[i]] = dis[st1[i]], lef[++cnt1] = st1[i];
    		for (i = 1; i <= tp2; ++i) val[st2[i]] = dis[st2[i]], rig[++cnt2] = st2[i];
    		Tree2 :: Solve(edge[e].w);
    		tmp1 = size[u1] > size[u2] ? sz - size[u2] : size[u1];
    		tmp2 = size[u2] > size[u1] ? sz - size[u1] : size[u2];
    		sz = tmp1, mn = n + 1, rte = -1, Getroot(u1, 0, -1), Solve(rte);
    		sz = tmp2, mn = n + 1, rte = -1, Getroot(u2, 0, -1), Solve(rte);
    	}
    
    	void Init() {
    		int u, v, i;
    		ll w;
    		memset(first, -1, sizeof(first));
    		memset(head, -1, sizeof(head));
    		m = n;
    		for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add1(u, v, w);
    		cnt = 0, Rebuild(1, 0);
    	}
    
    	void Calc() {
    		sz = n, mn = n + 1, Getroot(1, 0, -1), Solve(rte);
    	}
    }
    
    int main() {
    	int n;
    	scanf("%d", &n);
    	Tree1 :: n = Tree2 :: n = Tree3 :: n = n;
    	Tree1 :: Init(), Tree2 :: Init(), Tree3 :: Init();
    	Tree3 :: Calc(), printf("%lld
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    PHP 缓存技术
    redis雪崩
    【Redis】- 缓存击穿
    Memcache 与 Memcached 的区别
    数据库设计:范式与反范式
    Thinkphp5多数据库切换
    PHP 分布式集群中session共享问题以及session有效期的设置
    Nginx使用upstream实现动静分离
    rsync 服务快速部署手册
    tp5 为什么使用单例模式
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/10225425.html
Copyright © 2011-2022 走看看