Idiot 的間諜網絡

Idiot Idiot\ 的間諜網絡

題目鏈接:jzoj 3910

題目大意

有三種操作:

  1. 讀入:1 x y1\ x\ y:特工yy成爲特工xx的直接領導。數據保證在此之前特工xx沒有直接領導;
  2. 讀入:2 x2\ x:特工xx策劃了一起特別行動,然後上報其直接領導審批,之後其直接領導再上報其直接領導的直接領導審批,以此類推,直到某個特工審批後不再有直接領導;
  3. 讀入:3 x y3\ x\ y:詢問特工xx 是否直接策劃或審批過第yy次特別行動。所有特別行動按發生時間的順序從11開始依次編號。數據保證在詢問之前,第yy次特別行動已經發生過。(輸出YESYES或者NONO

樣例輸入

6 12
2 1
1 4 1
3 4 1
1 3 4
2 3
3 4 1
2 3
3 4 2
3 1 1
3 1 3
3 1 2
1 2 4

樣例輸出

NO
NO
YES
YES
YES
YES

數據範圍

對於30%30\% 的數據,n<=3103n <= 3 *10^3m<=5103m <= 5* 10^3
對於60%60\% 的數據,n<=2105n <=2 * 10^5m<=2105m <= 2 * 10^5
額外20%20\% 的數據,保證在任意時刻,整張間諜網絡由若干條互不相交的鏈構成;
對於100%100\% 的數據,n<=5105n <= 5 * 10^5m<=5105m <= 5 * 10^5
C++C++選手的程序在評測時使用編譯選項Wl;stack = 104857600-Wl;--stack\ =\ 104857600

思路

這是一道用到並查集和dfsdfs的問題。
AA知道BB乾的第xx件事,要滿足兩個點:

  1. AABB的祖先(或它自己)
  2. BB幹第xx件事時,AABB已經連通。(或者說AA已經是BB的祖先(或它本身))

首先,我們來康康第11個要求,這個要求我們可以用dfsdfs序來實現。
接着,第22個要求,可以讀入完之後,重新再“讀入一遍”。操作11就並查集確立祖先,這樣子操作22的時候就可以看他們的祖先是否相同。(相同就是連通的,否則不連通)
(說的有點亂,看代碼可能好點)

這樣,我們就可以知道答案了。

代碼

#include<cstdio>
#include<algorithm>

using namespace std;

struct note {
	int to, next;
}e[500001];
struct read {
	int what, num, who;
}a[500001];
int n, m, le[500001], z[500001], x[500001], y[500001], k, kk, kkk, in[500001], out[500001], father[500001];
bool fa[500001], ans[500001];

bool cmp(read x, read y) {//按詢問的任務從前到後排序
	return x.what < y.what;
}

void dfs(int now) {//求出dfs序
	in[now] = ++in[0];
	for (int i = le[now]; i; i = e[i].next) {
		dfs(e[i].to);
	}
	out[now] = ++out[0];
}

int find(int now) {//並查集
	if (father[now] == now) return now;
	return father[now] = find(father[now]);
}

int main() {
	scanf("%d %d", &n, &m);//讀入
	
	for (int i = 1; i <= m; i++) {
		scanf("%d", &z[i]);//讀入
		if (z[i] == 1) {
			scanf("%d %d", &x[i], &y[i]);//讀入
			e[++k] = (note){x[i], le[y[i]]}; le[y[i]] = k;//建鄰接表
			fa[x[i]] = 1;//找出所有根節點
		}
		else if (z[i] == 2) {
			scanf("%d", &x[i]);//讀入
		}
		else if (z[i] == 3) {
			scanf("%d %d", &x[i], &y[i]);//讀入
			a[++kk] = (read){y[i], ++kkk, x[i]};//記錄
		}
	}
	
	sort(a + 1, a + kk + 1, cmp);//按詢問的任務從前到後排序
	
	for (int i = 1; i <= n; i++) {//求出的dfs序,以便確定某個點是否是某一個點的祖先(或它本身)
		father[i] = i;//乘機初始化並查集
		if (!fa[i]) {
			dfs(i);
		}
	}
	
	int what_num = 0, what = 1;//初始化
	for (int i = 1; i <= m; i++) {
		if (z[i] == 1) father[find(x[i])] = find(y[i]);//連線
			else if (z[i] == 2) {
				what_num++;//到第幾個任務
				while (what <= kk && a[what].what <= what_num) {//是否還是那次任務
					if (find(a[what].who) == find(x[i]) && in[a[what].who] <= in[x[i]] && out[a[what].who] >= out[x[i]])//判斷是否連通,且是否是祖先
						ans[a[what].num] = 1;//標記
					what++;//到下一個詢問
				}
			}
	}
	
	for (int i = 1; i <= kk; i++)
		if (ans[i]) printf("YES\n");//知道
			else printf("NO\n"); //不知道
	
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章