问题描述
这天,CD 作为 moreD 的宠物,又被残酷地训练爬树了,moreD 保证了这棵树满足从任意一个点出发,CD 都能走到所有的点,CD 每天都要爬过所有的点才能回家吃饭。经过一天又一天残酷的训练以后,CD 已经忍无可忍了,于是CD 会愤怒地误伤一条树枝,一条树枝被误伤以后就不可以再走了。当然,CD 不符合宠物法则的行动怎么会逃过 moreD 的眼睛,moreD决定,每当CD 误伤一条树枝,他都会再重新加一条树枝,可是他不知道加完以后,这只宠物是否还能从任意一个点出发到达所有的点,要是不能,岂不是让这只宠物得逞了么?问题是,现在 moreD也不知道CD 最终会误伤哪一条树枝,于是现在 moreD 臆测出了许多种可能,你要告诉 moreD:如果CD 误伤了第 z条边,他再在编号为 x 的点和编号为 y 的点之间加一条边,CD 是否能得逞。
输入格式
第一行一个正整数 n,表示节点个数。
接下来 n-1 行,每行两个正整数 x,y,表示原来树上存在一条连接编号为 x的节点和编号为 y 的节点的边。第 n+1 行一个正整数 Q,表示询问次数。接下来 Q 行,每行三个正整数 x,y,z,表示一个询问(含义如题所示)。输出格式
输出共 Q 行。
对于每一个询问,如果 CD 会得逞就输出 YES,否则输出 NO。样例输入
5
1 2
2 3
2 4
4 5
3
2 5 3
2 3 1
1 5 2
样例输出
NO
YESYES数据范围
对于20%的数据保证 n,Q≤1000。
对于另外20%的数据保证 n,Q≤10000 且树为随机生成。
对于70%的数据保证 n,Q≤200000。
对于100%的数据保证 n≤200000,Q≤2000000。
题解
误伤一条边后,树变成两个连通块,如果新加的边可以把这两个连通块连起来,就满足能从任意一个点出发到达所有的点,也就是说CD不能得逞,否则CD就得逞了。(我在这边纠结了好久,每次都打反了(╥╯^╰╥))
假设在误伤边前加了一条边,就会在树上形成一个环,只要误伤的边在环中就不能得逞。所以不能得逞的条件就是新加入的那条边的两个端点在原树上面的路径包含删去的边。
一开始把树处理出来之后,只需要判断新加进去的边是否会连接由被误伤的边断开的连通块,被误伤的边会把原树断开,变成一个子树加另外一块。而这条边是连接这两个连通块的充要条件就是这条边有且仅有一个点在被割开的子树内。只要用 DFS 序判断是否仅有一个点的 DFS 序在被割开的子树所包含的范围内就可以了,时间复杂度是 O(N+Q)的。
然后用DFS序判断的时候我又纠结了好久。。。(ー`´ー)
三个if else 语句改了好多遍
最后发现其实好像没有我想的那么复杂。。。╮(╯▽╰)╭
区间(l,r)包含在区间(L,R)里面,就是(L<=l && r<=R),
不包含就是!(L<=l && r<=R)
然后一个包含一个不包含就不会很难写。。。┐( ̄ヮ ̄)┌
还有一个要注意的是,对于无向边我们存的是两条方向相反编号相邻的有向边,建图的时候边的编号从0开始,这样读入的第i条边对应的就是第((i-1)<<1)条和第(((i-1)<<1)^1)条边。【这边我就没有纠结了ε=(´ο`*))) 】
一个题解写了半个小时。。。ヽ(ー_ー)ノ
1 #include2 #include 3 const int maxn=200005; 4 struct node{ 5 int u,nex; 6 }g[400005]; 7 int n,Q,fir[200005],num=-1; 8 int f[200005][20],dfn[200005][2],cnt; 9 bool ct[400005];10 int min(int x,int y)11 {12 return x y?x:y;17 }18 void add(int x,int y)19 {20 g[++num].u=y; g[num].nex=fir[x]; fir[x]=num; 21 return;22 }23 void dfs(int x,int fa)24 {25 dfn[x][0]=++cnt;26 for (int k=fir[x];k>-1;k=g[k].nex)27 if (g[k].u!=fa)28 dfs(g[k].u,x);29 dfn[x][1]=++cnt;30 return;31 }32 int main()33 {34 int i,j,k,x,y,z,u,v,l,r;35 memset(fir,-1,sizeof(fir));36 scanf("%d",&n);37 for (i=1;i