zoukankan      html  css  js  c++  java
  • 【LG3242】 [HNOI2015]接水果

    题面

    洛谷

    题解

    20pts

    对于(n,P,Qleq 3000),暴力判断每条路径的包含关系然后排序(kth)即可,复杂度(O(PQlog P))

    另30pts

    原树为一条链。

    发现对于每个盘子,也就是区间(x,y),那么对于包含这个区间的水果(u,v),要满足(uleq xleq yleq v)

    将水果和盘子放在二维平面上一维排序,一维用数据结构维护即可。

    100pts

    设对于一个点(x),我们(dfs)时第一次访问的时间为(L_x),回溯时时间为(R_x)

    那么我们下面讨论一下路径之间的包含关系:

    对于路径(u,v)((dep_u<dep_v)),包含它的路径(x,y)有以下情况:

    1.(lca_{u,v} eq u),显然有(L_uleq L_xleq R_u),(L_vleq L_yleq R_v)

    可以看作点((L_x,L_y))包含在矩形({(L_u,L_v),(R_u,R_v)})中。

    2.(lca_{u,v}= u),设(w)为路径(u,v)(u)的儿子,那么显然有一个点在(v)的子树内,
    另一个点在除了(w)子树的其他地方,
    写成上面那样的关系,就是点((L_x,L_y))在矩形({(1,L_v),(L_w-1,R_v)}cup {(L_v,R_w+1),(R_v,n)})中。

    然后对于这个东西,整体二分+扫描线,看有几个在((L_x,L_y))上的权值在(midleq),按照整体二分的套路搞即可。

    代码

    #include <iostream> 
    #include <cstdio> 
    #include <cstdlib> 
    #include <cstring> 
    #include <cmath> 
    #include <algorithm> 
    using namespace std; 
    inline int gi() { 
        register int data = 0, w = 1; 
        register char ch = 0; 
        while (!isdigit(ch) && ch != '-') ch = getchar(); 
        if (ch == '-') w = -1, ch = getchar(); 
        while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
        return w * data; 
    } 
    const int MAX_N = 4e4 + 5; 
    struct Graph { int to, next; } e[MAX_N << 1]; int fir[MAX_N], e_cnt; 
    void clearGraph() { memset(fir, -1, sizeof(fir)); e_cnt = 0; } 
    void Add_Edge(int u, int v) { e[e_cnt] = (Graph){v, fir[u]}; fir[u] = e_cnt++; } 
    int N, P, Q; 
    int dep[MAX_N], L[MAX_N], R[MAX_N], tim; 
    int pa[17][MAX_N]; 
    void dfs(int x, int fa) {
    	dep[x] = dep[fa] + 1, L[x] = ++tim; 
    	for (int i = 0; i < 16; i++) pa[i + 1][x] = pa[i][pa[i][x]]; 
    	for (int i = fir[x]; ~i; i = e[i].next) { 
    		int v = e[i].to; if (v == fa) continue; 
    		pa[0][v] = x, dfs(v, x); 
    	} 
    	R[x] = tim; 
    } 
    int LCA(int u, int v) { 
    	if (dep[u] < dep[v]) swap(u, v); 
    	for (int i = 16; i >= 0; i--)
    		if ((1 << i) <= dep[u] - dep[v]) u = pa[i][u]; 
    	if (u == v) return u; 
    	for (int i = 16; i >= 0; i--) 
    		if (pa[i][u] != pa[i][v]) u = pa[i][u], v = pa[i][v]; 
    	return pa[0][u]; 
    } 
    int Jump(int x, int num) { 
    	for (int i = 16; i >= 0; i--) if ((num >> i) & 1) x = pa[i][x]; 
    	return x; 
    } 
    int q_cnt, p_cnt; 
    struct Line { int x, _y, y, op, val; } p[MAX_N << 2], lp[MAX_N << 2], rp[MAX_N << 2]; 
    bool operator < (const Line &l, const Line &r) { return l.x < r.x; } 
    struct Query { int x, y, k, id; } q[MAX_N], lq[MAX_N], rq[MAX_N]; 
    bool operator < (const Query &l, const Query &r) { return l.x < r.x; }
    inline int lb(int x) { return x & -x; } 
    int ans[MAX_N], c[MAX_N]; 
    void add(int x, int v) { while (x <= N) c[x] += v, x += lb(x); } 
    int sum(int x) { int res = 0; while (x > 0) res += c[x], x -= lb(x); return res; }
    int h[MAX_N], cnt = 0; 
    void Div(int lval, int rval, int sp, int tp, int sq, int tq) { 
    	if (sp > tp || sq > tq) return ; 
    	if (lval == rval) {
    		for (int i = sq; i <= tq; i++) ans[q[i].id] = h[lval];
    		return ; 
    	} 
    	int mid = (lval + rval) >> 1; 
    	int ql = 0, qr = 0, pl = 0, pr = 0, j = sp; 
    	for (int i = sq; i <= tq; i++) { 
    		for ( ; j <= tp && p[j].x <= q[i].x; j++) { 
    			if (p[j].val > h[mid]) rp[++pr] = p[j]; 
    			else add(p[j]._y, p[j].op), add(p[j].y + 1, -p[j].op), lp[++pl] = p[j]; 
    		} 
    		int tmp = sum(q[i].y); 
    		if (q[i].k > tmp) q[i].k -= tmp, rq[++qr] = q[i]; 
    		else lq[++ql] = q[i]; 
    	} 
    	for ( ; j <= tp; j++)
    		if (p[j].val > h[mid]) rp[++pr] = p[j]; 
    		else add(p[j]._y, p[j].op), add(p[j].y + 1, -p[j].op), lp[++pl] = p[j]; 
    	for (int i = 1; i <= pl; i++) add(lp[i]._y, -lp[i].op), add(lp[i].y + 1, lp[i].op); 
    	for (int i = 1; i <= pl; i++) p[i + sp - 1] = lp[i]; 
    	for (int i = 1; i <= pr; i++) p[sp + pl - 1 + i] = rp[i]; 
    	for (int i = 1; i <= ql; i++) q[i + sq - 1] = lq[i]; 
    	for (int i = 1; i <= qr; i++) q[sq + ql - 1 + i] = rq[i]; 
    	Div(lval, mid, sp, sp + pl - 1, sq, sq + ql - 1); 
    	Div(mid + 1, rval, sp + pl, tp, sq + ql, tq); 
    } 
    
    int main () { 
    #ifndef ONLINE_JUDGE 
        freopen("cpp.in", "r", stdin); 
    #endif 
    	clearGraph(); 
    	N = gi(), P = gi(), Q = gi(); 
    	for (int i = 1; i < N; i++) { 
    		int u = gi(), v = gi(); 
    		Add_Edge(u, v), Add_Edge(v, u); 
    	} 
    	dfs(1, 0); 
    	for (int i = 1; i <= P; i++) { 
    		int u = gi(), v = gi(); h[i] = gi(); 
    		if (L[u] > L[v]) swap(u, v); 
    		int lca = LCA(u, v); 
    		if (lca == u) { 
    			int z = Jump(v, dep[v] - dep[u] - 1); 
    			p[++p_cnt] = (Line){1, L[v], R[v], 1, h[i]}; 
    			p[++p_cnt] = (Line){L[z], L[v], R[v], -1, h[i]}; 
    			if (R[z] < N) { 
    				p[++p_cnt] = (Line){L[v], R[z] + 1, N, 1, h[i]}; 
    			    p[++p_cnt] = (Line){R[v] + 1, R[z] + 1, N, -1, h[i]}; 
    			} 
    		} else {
    			p[++p_cnt] = (Line){L[u], L[v], R[v], 1, h[i]}; 
    			p[++p_cnt] = (Line){R[u] + 1, L[v], R[v], -1, h[i]}; 
    		} 
    	} 
    	sort(&p[1], &p[p_cnt + 1]); 
    	sort(&h[1], &h[P + 1]); cnt = unique(&h[1], &h[P + 1]) - h - 1; 
    	while (Q--) { 
    		int u = gi(), v = gi(), k = gi(); 
    		if (L[u] > L[v]) swap(u, v); 
    		q[++q_cnt] = (Query){L[u], L[v], k, q_cnt}; 
    	} 
    	sort(&q[1], &q[q_cnt + 1]); 
    	Div(1, cnt, 1, p_cnt, 1, q_cnt); 
    	for (int i = 1; i <= q_cnt; i++) printf("%d
    ", ans[i]); 
        return 0; 
    } 
    
  • 相关阅读:
    Fedora/CentOS使用技巧
    Haproxy配置
    iscsi使用教程
    Linux网络配置
    Linux命令使用
    luogu-1908 逆序对 离散化+树状数组
    算法题目签到表
    [笔记-机器学习]贝叶斯分类器的原理及实现
    [笔记-数据结构]树状数组
    [笔记-数据结构]哈希表
  • 原文地址:https://www.cnblogs.com/heyujun/p/10431631.html
Copyright © 2011-2022 走看看