http://codeforces.com/contest/812/problem/D
题意:
现在有n个孩子,m个玩具,每次输入x y,表示x孩子想要y玩具,如果y玩具没人玩,那么x就可以去玩,如果y有人玩的话,x就必须等待y玩完后才能玩。如果出现循环,那么这个循环里的孩子都会哭。
现在有q次询问,如果加入x y,会有多少孩子哭。
思路:
建立一棵树,根结点就是第一个玩玩具y的人,它的子树就是等待玩具的人(子树按照等待顺序建树)。这样就会形成很多棵树。
这样的话,对于每个询问,我们去找到最后一个玩y玩具的人,只有y玩完了那才会给x,那么这样的话这两个人也应该连一条边,如果连边后形成了环,那么这个环里的顶点数就是哭的孩子数。
其实这就是判断祖先的问题,如果x是最后一个玩y玩具的人的祖先,那么他们连边后肯定是要形成环的。
那么怎么判断两个点的祖先关系呢?
对树dfs,记录每个顶点的访问次序标号in和结束访问标号out,如果x是y的祖先就需要满足in【i】<in【y】<out【i】
在这道题目中每个人最多只会等待一个玩具,所以在树中,每个结点最多只有一个子节点,那么我们就可以根据out和in标记来计算环中的顶点个数。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<sstream> 6 #include<vector> 7 #include<stack> 8 #include<queue> 9 #include<cmath> 10 #include<map> 11 using namespace std; 12 typedef long long ll; 13 typedef pair<int,long long> pll; 14 const int INF = 0x3f3f3f3f; 15 const int maxn=1e5+5; 16 17 int n,m,k,q; 18 vector<int> g[maxn]; 19 int in[maxn],out[maxn]; 20 int vis[maxn]; 21 int dfs_clock; 22 23 //记录访问标记和结束标记 24 void dfs(int u) 25 { 26 in[u]=++dfs_clock; 27 for(int i=0;i<g[u].size();i++) 28 { 29 dfs(g[u][i]); 30 } 31 out[u]=dfs_clock; 32 } 33 34 int main() 35 { 36 //freopen("input.txt","r",stdin); 37 scanf("%d%d%d%d",&n,&m,&k,&q); 38 for(int i=1;i<=k;i++) 39 { 40 int x,y; 41 scanf("%d%d",&x,&y); 42 if(vis[y]) 43 { 44 g[vis[y]].push_back(x); //x需要等待,vis[y]玩好后给x 45 } 46 vis[y]=x; 47 } 48 dfs_clock=0; 49 for(int i=1;i<=n;i++) 50 { 51 if(!in[i]) 52 dfs(i); 53 } 54 55 while(q--) 56 { 57 int x,y; 58 scanf("%d%d",&x,&y); 59 y=vis[y]; //找到最后一个玩y的人 60 if(in[x]<=in[y] && out[x]>=in[y]) 61 printf("%d ",out[x]-in[x]+1); 62 else 63 puts("0"); 64 } 65 66 return 0; 67 }