• BZOJ2870 最长道路tree(边分治)


    BZOJ2870 最长道路tree(边分治)

    题目描述

    给定一棵N个点的树,求树上一条链使得链的长度乘链上所有点中的最小权值所得的积最大。

    其中链长度定义为链上点的个数。

    数据范围

    (1 le n le 50000)

    解题思路

    经典的边分治题。

    首先暴力边分治会被卡成 (n^2) 的复杂度

    所以我们需要把它建成二叉树,具体来说就是一个点第一个儿子直接连向他,剩下的建一个虚点顺次相连,每个虚点下面挂一个儿子,虚点的信息随题目的不同也不同

    在这道题中,考虑边分治分成两个集合,两个集合排序然后双指针可以轻松求出答案,维护信息一定要把距离扔到边上,否则跨过虚儿子不会增加答案,另要注意分治下去的时候要记录一些全局变量的信息防止变化

    代码如下

    const int N = 200500;
    int h[N], ne[N<<1], to[N<<1], tot, cnt;
    inline void add(int x, int y) {
    	ne[++tot] = h[x], to[h[x] = tot] = y;
    }
    
    ll val[N], las[N], ans, n;
    namespace Conquer {
    	int h[N], ne[N<<1], to[N<<1], w[N<<1], tot = 1;
    	inline void add(int x, int y, int z) {
    		ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
    	}
    	inline void adde(int x, int y, int z) { add(x, y, z), add(y, x, z); }
    	
    	int vis[N<<1], siz[N], Siz, lim, ed;
    	void dfs(int x, int fa) {
    		siz[x] = 1;
    		for (int i = h[x], y; i; i = ne[i]) {
    			if ((y = to[i]) == fa || vis[i]) continue;
    			dfs(y, x), siz[x] += siz[y];
    			int tp = max(siz[y], Siz - siz[y]);
    			if (tp < lim) lim = tp, ed = i;
    		}
    	}
    	pair<ll, ll> A[N], B[N], *f; 
    	int *t, ta, tb;
    	void dfs(int x, int fa, ll mn, ll dis) {
    		Mn(mn, val[x]), f[++*t] = MP(mn, dis);
    		for (int i = h[x], y; i; i = ne[i]) {
    			if (vis[i] || (y = to[i]) == fa) continue;
    			dfs(y, x, mn, dis + w[i]);
    		}
    	}
    	void solve(int x, int S) {
    		if (S <= 1) return;
    		Siz = lim = S, dfs(x, 0), vis[ed] = vis[ed^1] = 1;
    		t = &ta, ta = 0, f = A, dfs(to[ed], 0, 1e9, 0);
    		t = &tb, tb = 0, f = B, dfs(to[ed^1], 0, 1e9, 0);
    		sort(A + 1, A + ta + 1), sort(B + 1, B + tb + 1);
    		ll j = tb, mx = 0, l = w[ed];
    		for (int i = ta;i >= 1; i--) {
    			while (j >= 1 && B[j].fi >= A[i].fi) Mx(mx, B[j--].se);
    			if (j < tb) Mx(ans, (mx + A[i].se + 1 + l) * A[i].fi);
    		}
    		j = ta, mx = 0;
    		for (int i = tb;i >= 1; i--) {
    			while (j >= 1 && A[j].fi >= B[i].fi) Mx(mx, A[j--].se);
    			if (j < ta) Mx(ans, (mx + B[i].se + 1 + l) * B[i].fi);
    		}
    		int tx = to[ed], ty = to[ed^1];
    		solve(ty, S - siz[tx]), solve(tx, siz[tx]);
    	}
    }
    
    void dfs(int x, int fa) {
    	for (int i = h[x], y; i; i = ne[i]) {
    		if ((y = to[i]) == fa) continue; dfs(y, x);
    		if (!las[x]) Conquer::adde(x, y, 1), las[x] = x;
    		else {
    			val[++cnt] = val[x];
    			Conquer::adde(las[x], cnt, 0);
    			Conquer::adde(las[x] = cnt, y, 1);
    		}
    	}
    }
    
    int main() {
    	read(n), cnt = n;
    	for (int i = 1;i <= n; i++) read(val[i]);
    	for (int i = 1, x, y;i < n; i++)
    		read(x), read(y), add(x, y), add(y, x);
    	dfs(1, 0), Conquer::solve(1, cnt);
    	write(ans);
    	return 0;
    }
    
    
  • 相关阅读:
    【算法】Kruskal算法(解决最小生成树问题) 含代码实现
    POJ 1182 食物链 (并查集解法)(详细注释)
    APICloud关闭Key Building Resolve
    ubuntu配置国内源
    缓存穿透、缓存击穿、缓存雪崩概念及解决方案
    POST请求和GET请求的区别
    ibatis 中#和 $ 符号的区别
    自动装箱和自动拆箱理解
    回文串算法说明(带注释)
    Object 对象有哪些方法?
  • 原文地址:https://www.cnblogs.com/Hs-black/p/13363636.html
Copyright © 2020-2023  润新知