zoukankan      html  css  js  c++  java
  • 「HNOI2015」接水果

    「HNOI2015」接水果

    题意:

    给你一个树上路径集合$ S $ ,每条路径有个权值。每次询问一条路径 $ (x, y) $ ,问它在 $ S $ 中包含的路径中权值第 $ k $ 小的是多少。

    做法:

    首先考虑如何判断判断一条路径是否被另一条路径包含。
    当一条路径 $ (x, y) (dep_x < dep_y) $ 是一条深度从浅到深的链时,路径 $ (u, v) (dep_u < dep_v) $ 包含路径 $ (x, y) $ 满足 $ v $ 在 $ y $ 的子树内、 $ u $ 在 $ x $ 的孩子、 $ y $ 的祖先 的子树外, $ u, v $ 在树的dfn序上为连续几段。
    当一条路径 $ (x, y) (dep_x < dep_y) $ 不是一条深度从浅到深的链时,路径 $ (u, v) $ 包含路径 $ (x, y) $ 满足 $ v $ 在 $ y $ 的子树内、 $ u $ 在 $ x $ 的祖先 的子树外, $ u, v $ 在树的dfn序上为连续几段。
    然后树套树维护路径 $ (x, y) $ 包含多少路径,树套树维护即可。

    对于每一个询问,我们都可以二分答案。将集合 $ S $ 中的路径按权值排序,然后查询其中包含的排名即可。我们发现这可以整体二分。时间复杂度 $ O(n log^3{n}) $

    在Luogu上开O2快如风,在LOJ上被1s时限卡自闭。。。

    其实树套树可以用扫描线代替的,时间复杂度 $ O(n log^2{n}) $。

    #include <bits/stdc++.h>
    #define pb push_back
    #define LNK(i, a) for(int i = hed[a]; i; i = nxt[i])
    using namespace std;
    const int N = 40010;
    int n, m, q;
    int cnt = 0, hed[N], to[N + N], nxt[N + N];
    int dfn[N], low[N], idx = 0, fa[16][N], dep[N];
    struct Plate {
    	int x, y, w;
    	inline bool operator<(const Plate &yy)const { return w < yy.w; }
    }; Plate a[N];
    struct Fruit {
    	int x, y, k, id;
    	inline bool operator<(const Fruit &yy)const { return x < yy.x; }
    };
    int pp[N], lp = 0, ans[N];
    
    namespace Tree {
    	struct Line {
    		int x, y, w;
    		Line(int X = 0, int Y = 0, int W = 0) : x(X), y(Y), w(W) {}
    		inline bool operator<(const Line &yy)const { return x < yy.x; }
    	}; Line t[N * 20]; int tot = 0;
    	int tr[N];
    	
    	inline void add(int x, int w) { for(; x <= n; x += x & -x) tr[x] += w; }
    	inline int ask(int x) {
    		int ss = 0; for(; x; x -= x & -x) ss += tr[x]; return ss;
    	}
    	inline void mdy(int x, int y, int xx, int yy, int w) {
    		t[++tot] = Line(x, xx, w), t[++tot] = Line(x, yy + 1, -w);
    		t[++tot] = Line(y + 1, xx, -w), t[++tot] = Line(y + 1, yy + 1, w);
    	}
    }
    
    void gi(int &x) {
    	x = 0; register char c = getchar();
    	for(; c < '0' || c > '9'; c = getchar());
    	for(; c >= '0' && c <= '9';)
    		x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    }
    inline void add(int x, int y) {
    	to[++cnt] = y, nxt[cnt] = hed[x], hed[x] = cnt;
    }
    void dfs(int u, int ff) {
    	fa[0][u] = ff, dep[u] = dep[ff] + 1, dfn[u] = ++idx;
    	for(int i = 1; i < 16; ++i) fa[i][u] = fa[i - 1][fa[i - 1][u]];
    	LNK(i, u) if(to[i] ^ ff) dfs(to[i], u); low[u] = idx;
    }
    int LCA(int x, int y) {
    	if(dep[x] < dep[y]) swap(x, y);
    	for(int i = 15; ~i; --i) if(dep[fa[i][x]] >= dep[y]) x = fa[i][x];
    	return x == y;
    }
    int find(int x, int y) {
    	for(int i = 15; ~i; --i) if(dep[fa[i][y]] > dep[x]) y = fa[i][y];
    	return y;
    }
    void solve(vector<Fruit> Q, int l, int r, int L, int R) {
    	if(l >= r) {
    		for(int i = 0; i < Q.size(); ++i) ans[Q[i].id] = pp[l]; return ;
    	}
    	int mid = (l + r) >> 1, pos; vector<Fruit> ls, rs;
    	for(int i = L, lca, x, y; i <= R; ++i) {
    		if(a[i].w > pp[mid]) break;
    		pos = i, x = a[i].x, y = a[i].y, lca = LCA(x, y);
    		if(lca) {
    			x = find(x, y);
    			Tree::mdy(dfn[y], low[y], 1, dfn[x] - 1, 1);
    			Tree::mdy(dfn[y], low[y], low[x] + 1, n, 1);
    			Tree::mdy(1, dfn[x] - 1, dfn[y], low[y], 1);
    			Tree::mdy(low[x] + 1, n, dfn[y], low[y], 1);
    		}
    		else {
    			Tree::mdy(dfn[x], low[x], dfn[y], low[y], 1);
    			Tree::mdy(dfn[y], low[y], dfn[x], low[x], 1);
    		}
    	}
    	sort(Tree::t + 1, Tree::t + Tree::tot + 1); int j = 1;
    	for(int i = 0, K; i < Q.size(); ++i) {
    		for(; j <= Tree::tot && Tree::t[j].x <= Q[i].x; ++j)
    			Tree::add(Tree::t[j].y, Tree::t[j].w);
    		K = Tree::ask(Q[i].y);
    		K >= Q[i].k ? ls.pb(Q[i]) : (Q[i].k -= K, rs.pb(Q[i]));
    	}
    	for(; j <= Tree::tot; ++j) Tree::add(Tree::t[j].y, Tree::t[j].w);
    	Tree::tot = 0;
    	solve(ls, l, mid, L, pos), solve(rs, mid + 1, r, pos + 1, R);
    }
    int main() {
    	gi(n), gi(m), gi(q);
    	for(int i = 1, x, y; i < n; ++i) gi(x), gi(y), add(x, y), add(y, x);
    	dfs(1, 0);
    	for(int i = 1; i <= m; ++i) {
    		gi(a[i].x), gi(a[i].y), gi(a[i].w);
    		if(dep[a[i].x] > dep[a[i].y]) swap(a[i].x, a[i].y);
    	}
    	sort(a + 1, a + m + 1), pp[++lp] = a[1].w;
    	for(int i = 2; i <= m; ++i) if(a[i].w ^ a[i - 1].w) pp[++lp] = a[i].w;
    	vector<Fruit> Q;
    	for(int i = 1, x, y, k; i <= q; ++i)
    		gi(x), gi(y), gi(k), Q.pb((Fruit){dfn[x], dfn[y], k, i});
    	sort(Q.begin(), Q.end());
    	solve(Q, 1, lp, 1, m); for(int i = 1; i <= q; ++i) printf("%d
    ", ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    剑指Offer 07 重建二叉树
    剑指Offer 06 从尾到头打印链表
    剑指Offer 05 替换空格
    剑指Offer 04 二维数组中的查找
    剑指Offer 03 数组中重复的数字
    leetcode518
    leetcode474
    leetcode376
    leetcode646
    leetcode213
  • 原文地址:https://www.cnblogs.com/daniel14311531/p/10626248.html
Copyright © 2011-2022 走看看