zoukankan      html  css  js  c++  java
  • P4899 [IOI2018] werewolf 狼人

    题目描述

    在日本的茨城县内共有(N)个城市和(M)条道路。这些城市是根据人口数量的升序排列的,依次编号为(0)(N-1)。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。

    你计划了(Q)个行程,这些行程分别编号为(0)(Q-1)。第(i(0 leq i leq Q - 1))个行程是从城市(S_i)到城市(E_i)​。

    你是一个狼人。你有两种形态:人形和狼形。在每个行程开始的时候,你是人形。在每个行程结束的时候,你必须是狼形。在行程中,你必须要变身(从人形变成狼形)恰好一次,而且只能在某个城市内(包括可能是在(S_i)(E_i)内)变身。

    狼人的生活并不容易。当你是人形时,你必须避开人少的城市,而当你是狼形时,你必须避开人多的城市。对于每一次行程(i(0 leq i leq Q - 1)),都有两个阈值 (L_i)(R_i(0 leq L_i leq R_i leq N - 1)),用以表示哪些城市必须要避开。准确地说,当你是人形时,你必须避开城市(0, 1, ldots , L_i - 1);而当你是狼形时,则必须避开城市(R_i + 1, R_i + 2, ldots , N - 1)。这就是说,在行程(i)中,你必须在城市(L_i, L_i + 1, ldots , R_i)中的其中一个城市内变身。

    你的任务是,对每一次行程,判定是否有可能在满足上述限制的前提下,由城市(S_i)走到城市(E_i)​。你的路线可以有任意长度。

    题解

    首先出于个人习惯问题将城市的编号统一加(1)变成([1, N])

    对于人形相当于从(S)出发只能走([L, N]),而对于狼形相当于从(T)出发只能走([1, R])

    我们(Kruskal)建两棵树(A)(B)。其中(A)满足子节点的编号大于父节点,(B)满足子节点的编号小于父节点,那么我们可以在两棵树上分别从(S)(T)倍增,找到深度最小的满足两个区间限制的点,那么他的子树就都是合法的。

    考虑一整棵子树的(dfs)序是一段区间,所以我们将点的编号的匹配转化成两段(dfs)序的匹配,看是否有点的(dfs)序分别在两棵树上满足两个区间。

    这个过程我们可以使用离线树状数组类似二维数点实现,也可以用主席树以(A)树上的(dfs)序为外层下标,(B)树上的(dfs)序为内层下标实现。

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 2e5 + 5;
    int n, m, Q, tot, head1[N], num, tr[N << 2], ans[N << 2];
    struct node{int to, nex;}b[N << 2];
    struct data{int x, y, val, id;}d[N << 2];
    inline int read()
    {
    	int x = 0, f = 1; char ch = getchar();
    	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
    	return x * f;
    }
    struct tree1
    {
    	int tot, head[N], fa[N], cnt, f[N][19], num, dfn[N], siz[N];
    	struct data{int to, nex;}a[N];
    	void add(int x, int y) {a[++ tot].to = y; a[tot].nex = head[x]; head[x] = tot;}
    	int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
    	void build()
    	{
    		for(int i = 1; i <= n; i ++) fa[i] = i;
    		for(int i = n, x, y; i >= 1; i --) // n -- > 1
    		{
    			x = i;
    			for(int j = head1[i]; j; j = b[j].nex)
    			{
    				y = b[j].to;
    				if(x < y)
    				{
    					int xx = find(x), yy = find(y);
    					if(xx == yy) continue;
    					fa[yy] = xx; add(xx, yy);
    					if((++ cnt) == n - 1) return;
    				}
    			}
    		}
    	}
    	void dfs(int x, int fat)
    	{
    		f[x][0] = fat; siz[x] = 1; dfn[x] = ++ num;
    		for(int i = 1; i <= 18; i ++) f[x][i] = f[f[x][i - 1]][i - 1];
    		for(int i = head[x]; i; i = a[i].nex)
    		{
    			int y = a[i].to;
    			if(y == fat) continue;
    			dfs(y, x); siz[x] += siz[y];
    		}
    	}
    	int query(int x, int em)
    	{
    		for(int i = 18; i >= 0; i --) if(f[x][i] && f[x][i] >= em) x = f[x][i];
    		return x;
    	}
    }A;
    struct tree2
    {
    	int tot, head[N], fa[N], cnt, f[N][19], num, dfn[N], siz[N];
    	struct data{int to, nex;}a[N];
    	void add(int x, int y) {a[++ tot].to = y; a[tot].nex = head[x]; head[x] = tot;}
    	int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
    	void build()
    	{
    		for(int i = 1; i <= n; i ++) fa[i] = i;
    		for(int i = 1, x, y; i <= n; i ++) // 1 -- > n
    		{
    			x = i;
    			for(int j = head1[i]; j; j = b[j].nex)
    			{
    				y = b[j].to;
    				if(x > y)
    				{
    					int xx = find(x), yy = find(y);
    					if(xx == yy) continue;
    					fa[yy] = xx; add(xx, yy);
    					if((++ cnt) == n - 1) return;
    				}
    			}
    		}
    	}
    	void dfs(int x, int fat)
    	{
    		f[x][0] = fat; siz[x] = 1; dfn[x] = ++ num;
    		for(int i = 1; i <= 18; i ++) f[x][i] = f[f[x][i - 1]][i - 1];
    		for(int i = head[x]; i; i = a[i].nex)
    		{
    			int y = a[i].to;
    			if(y == fat) continue;
    			dfs(y, x); siz[x] += siz[y];
    		}
    	}
    	int query(int x, int em)
    	{
    		for(int i = 18; i >= 0; i --) if(f[x][i] && f[x][i] <= em) x = f[x][i];
    		return x;
    	}
    }B;
    void add(int x, int y) {b[++ tot].to = y; b[tot].nex = head1[x]; head1[x] = tot;}
    bool cmp(const data & a, const data & b) {return a.x == b.x ? a.id < b.id : a.x < b.x;}
    int lowbit(int x) {return x & (- x);}
    void add(int x) {while(x <= n) {tr[x] ++; x += lowbit(x);}}
    int query(int x) {int res = 0; while(x) {res += tr[x]; x -= lowbit(x);} return res;}
    void work()
    {
    	n = read(); m = read(); Q = read();
    	for(int i = 1, x, y; i <= m; i ++)
    	{
    		x = read() + 1; y = read() + 1;
    		add(x, y); add(y, x);
    	}
    	A.build(); B.build(); A.dfs(1, 0); B.dfs(n, 0);
    	for(int i = 1; i <= n; i ++) d[++ num] = data{A.dfn[i], B.dfn[i], 0, 0};
    	for(int i = 1, s, t, l, r; i <= Q; i ++)
    	{
    		s = read() + 1; t = read() + 1; l = read() + 1; r = read() + 1;
    		s = A.query(s, l); t = B.query(t, r);
    		int x = A.dfn[s] - 1, y = A.dfn[s] + A.siz[s] - 1;
    		int xx = B.dfn[t] - 1, yy = B.dfn[t] + B.siz[t] - 1;
    		d[++ num] = data{x, xx, 1, i}; d[++ num] = data{x, yy, -1, i};
    		d[++ num] = data{y, xx, -1, i}; d[++ num] = data{y, yy, 1, i};//
    	}
    	sort(d + 1, d + num + 1, cmp);
    	for(int i = 1; i <= num; i ++) if(d[i].val == 0) add(d[i].y); else ans[d[i].id] += d[i].val * query(d[i].y);
    	for(int i = 1; i <= Q; i ++) printf("%d
    ", ans[i] == 0 ? 0 : 1);
    }
    int main() {return work(), 0;}
    
  • 相关阅读:
    Flask-1-05-Cookie&Session
    Flask-1-05-Response
    Flask-1-04-Request
    Python3 装饰器无参有参 Egon老师视频
    python魔法方法、构造函数、序列与映射、迭代器、生成器---python基础教程
    Python3(廖雪峰教程) 链接
    Python面向对象之私有属性和方法(转载)
    python3 isinstance
    生成器 讲得不错的帖子
    Python3 pickle模块
  • 原文地址:https://www.cnblogs.com/Sunny-r/p/12582160.html
Copyright © 2011-2022 走看看