• 【luogu P4899】werewolf 狼人(最小生成树)(主席树)


    werewolf 狼人

    题目链接:luogu P4899

    题目大意

    给你一个无向图,然后每次要从一个地方走到另外一个地方。
    然后你在走的过程中要在一个点转换形态,转换之前你只能走大于等于 L 的点,转换之后你只能走小于等于 R 的点。
    然后问你对于每次询问,要你回答能不能走。

    思路

    首先我们考虑把要走的路分成两段。
    在转换之前走的,在转化之后走的。

    那我们考虑这么搞,求出转换之前能到的,求出转化之后能从这个地方到终点的(由于是双向边,所以也是从终点能到的)。
    然后如果这两个点集有交的话,我们就可以到。

    那接着看如何搞,首先不难看出两段其实是同样的道理,你反过来一下就好了。
    那就只看转换之前能到的。
    那看到只能走大于等于的,那我们考虑这么搞。

    用最小生成树的方法,从大到小枚举点,再枚举另一边比它大的边,然后建树,而且让小的作为父亲。
    然后你就会发现,(x) 子树内的点,就是你保证走的点大于等于 (x),而且在 (x) 出发能到的点。

    然后你考虑在这道题里面,那你从 (x) 出发,然后要大于等于 (l),那你就可以不断的从 (x) 往上跳,跳到不能跳,也就是如果跳了跳到的位置就 (<l)。然后再从那个位置的子树,就是你可以到的点了。

    然后问题就变成两个树的两个子树,两个数的点有对应关系,问你这两个子树里面是否有一对对应的点。

    那子树嘛,自然就想到是 dfs 序上的一段区间。

    那我们可以搞个二维网格,如果一对是 ((x,y)),那这个位置就是 (1)
    然后假设你两个数的两个 dfs 序上区间分别是 ([x1,y1],[x2,y2]),那就是求这个矩阵里面有没有 (1)

    然后发现每个行或列只会有一个 (1),所以我们考虑用主席树来搞。
    然后就好啦。

    代码

    #include<cstdio>
    #include<algorithm>
    
    using namespace std;
    
    const int N = 200001;
    const int M = 400001;
    const int Q = 200001;
    
    struct node {
    	int to, nxt;
    }e[M << 1];
    int n, m, q, x, y, le[N], KK, l, r;
    
    struct ababtree {
    	node ee[N];
    	int lee[N], KKK, fa[N][21], fath[N];
    	int dfn[N], tmp, st[N], ed[N], deg[N];
    	
    	void clean() {
    		for (int i = 1; i <= n; i++) fath[i] = i;
    	}
    	
    	int find(int now) {
    		if (fath[now] == now) return now;
    		return fath[now] = find(fath[now]);
    	}
    	
    	void add(int x, int y) {
    		ee[++KKK] = (node){y, lee[x]}; lee[x] = KKK;
    	}
    	
    	void build(bool op) {//搞最小生成树
    		int kn = 0;
    		if (!op) {
    			for (int i = n; i >= 1; i--) {
    				for (int j = le[i]; j; j = e[j].nxt)
    					if (e[j].to > i) {
    						int x = find(i), y = find(e[j].to);
    						if (x != y) {
    							fath[y] = x; add(x, y);
    							kn++; if (kn == n - 1) return ;
    						}
    					}
    			}
    		}
    		else {
    			for (int i = 1; i <= n; i++) {
    				for (int j = le[i]; j; j = e[j].nxt)
    					if (e[j].to < i) {
    						int x = find(i), y = find(e[j].to);
    						if (x != y) {
    							fath[y] = x; add(x, y);
    							kn++; if (kn == n - 1) return ;
    						}
    					}
    			}
    		}
    	}
    	
    	void dfs(int now, int father) {
    		st[now] = ++tmp; dfn[tmp] = now;
    		fa[now][0] = father;
    		deg[now] = deg[father] + 1;
    		for (int i = lee[now]; i; i = ee[i].nxt)
    			dfs(ee[i].to, now);
    		ed[now] = tmp;
    	}
    	
    	void bz() {
    		for (int i = 1; i <= 20; i++)
    			for (int j = 1; j <= n; j++)
    				fa[j][i] = fa[fa[j][i - 1]][i - 1];
    	}
    	
    	int jmp(int x, int y, bool op) {//跳到临界点
    		if (!op) {
    			for (int i = 20; i >= 0; i--)
    				if (fa[x][i] >= y) x = fa[x][i];
    		}
    		else {
    			for (int i = 20; i >= 0; i--)
    				if (fa[x][i] && fa[x][i] <= y) x = fa[x][i];
    		}
    		return x;
    	}
    }A, B;
    
    struct zxtree {//主席树
    	int ls[N << 6], rs[N << 6], val[N << 6], tot;
    	
    	int copy(int x) {
    		int y = ++tot;
    		ls[y] = ls[x]; rs[y] = rs[x]; val[y] = val[x];
    		return y;
    	}
    	
    	void up(int now) {
    		val[now] = val[ls[now]] + val[rs[now]];
    	}
    	
    	int insert(int now, int l, int r, int pl, int va) {
    		now = copy(now);
    		
    		if (l == r) {
    			val[now] += va;
    			return now;
    		}
    		
    		int mid = (l + r) >> 1;
    		if (pl <= mid) ls[now] = insert(ls[now], l, mid, pl, va);
    			else rs[now] = insert(rs[now], mid + 1, r, pl, va);
    		
    		up(now);
    		return now;
    	}
    	
    	int query(int now, int l, int r, int L, int R) {
    		if (!now) return 0;
    		if (L <= l && r <= R) return val[now];
    		
    		int mid = (l + r) >> 1, re = 0;
    		if (L <= mid) re += query(ls[now], l, mid, L, R);
    		if (mid < R) re += query(rs[now], mid + 1, r, L, R);
    		return re;
    	}
    }T;
    int rt[N];
    
    void add(int x, int y) {
    	e[++KK] = (node){y, le[x]}; le[x] = KK;
    	e[++KK] = (node){x, le[y]}; le[y] = KK;
    }
    
    int main() {
    	scanf("%d %d %d", &n, &m, &q);
    	for (int i = 1; i <= m; i++) {
    		scanf("%d %d", &x, &y); x++; y++;
    		add(x, y);
    	}
    	
    	A.clean(); B.clean();
    	A.build(0); B.build(1);
    	A.dfs(1, 0); B.dfs(n, 0);
    	A.bz(); B.bz();
    	
    	for (int i = 1; i <= n; i++) {//对应的位置
    		rt[i] = T.insert(rt[i - 1], 1, n, B.st[A.dfn[i]], 1);
    	}
    	
    	while(q--) {
    		scanf("%d %d %d %d", &x, &y, &l, &r);
    		x++; y++; l++; r++;
    		l = A.jmp(x, l, 0); r = B.jmp(y, r, 1);
    		if (T.query(rt[A.ed[l]], 1, n, B.st[r], B.ed[r]) - T.query(rt[A.st[l] - 1], 1, n, B.st[r], B.ed[r])) printf("1
    ");
    			else printf("0
    ");
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    官方文档翻译-Today
    RAC & MVVM 学习资料整理
    35种常用字体
    中文字体的种类
    自言自语(三)--部分中文字体
    自言自语(二)--英文无衬线体和有衬线体
    sketch字体设置技巧(一)---通过锚点改变字体形态
    提高设计档次的8个方法
    知识汇总09~bootstrap-select在Vue中的封装
    知识汇总08~字符串截取
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P4899.html
Copyright © 2020-2023  润新知