ACM/CCPC 历届真题 题解目录
Problem B. Gift Node
Time Limit: 1000ms Memory Limit: 512MB
Description
You are given a tree with n nodes (numbered from 1 to n). The root is node 1. Each edge in this tree has a particular color which is specified by an integer from 1 to 1 0 8 10^8 108. We define a path on the tree as a gift path if and only if adjacent edges on the path have different colors. In addition, we define a node as a gift node, if all the paths starting at this node are gift paths.
We have Q questions for you. For each question, you are given a node R, and you need to find out the number of gift nodes in the subtree of node R. Note that when we say a node in some subtree is a gift node, only paths in that subtree will be considered.
Input
First line contains an integer n (1 ≤ n ≤ 200000) .
Each of the next n − 1 lines contains three integers A i A_i Ai, B i B_i Bi, C i C_i Ci (1 ≤ A i A_i Ai, B i B_i Bi, C i C_i Ci ≤ n, A i A_i Ai, ≠ B i B_i Bi) , represents an edge of color C i C_i Ci that connects nodes A i A_i Ai and B i B_i Bi.
Then there will be a line which contains a single integer Q (1 ≤ Q ≤ 10000) , represents the number of questions.
Each of the next Q lines contains a single integer R i R_i Ri (1 ≤ R i R_i Ri ≤ n) , we want to know the number of gift nodes in the subtree where the root is R i R_i Ri.
Output
Print Q lines, each line contains a single integer S i S_i Si, represents
the answer for question i.
Sample
Input
8
1 3 1
2 3 1
3 4 3
4 5 4
5 6 3
6 7 2
6 8 2
2
1
5
Output
4
2
Hint
In the first question, the gift nodes are 3, 4, 5 and 6.
In the second question, the gift nodes are 5 and 6.
题目大意:
如果树上的一条路径的相邻边权值不同,则该路径为礼品路径。
以某个节点为根节点的树中,如果每条路径都是礼品路径,则该节点为礼品节点。
求以某节点为根节点的树中(包含该节点)礼品节点的个数。
分析:
考虑相邻两条边组成的边对(u-v, v-x)在树上的情况。
DFS对不同情况分别处理计数即可。
思路:
如果路径包含的边数大于等于2,比如u-v-x-……,那么我们只需要判断(u-v, v-x)边对是否相同即可,后续路径只要判断节点v是否为为礼品节点,就能知道后续路径是否为礼品路径了。
由样例解释可以看出,叶子节点不是礼品节点。
综上,可以先假设所有节点都是礼品节点,再判断是否有不符合条件的。
由于深搜的特性,判断礼品节点的过程和求以某个节点为根的树中有几个礼品节点的过程可以同时进行(自己思考一下为什么)。
代码如下:
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define vpii vector< pair<int, int> >
const int maxn = 2e5 + 5;
int n, q, r;
vpii edge[maxn]; //edge[u]<v, w>代表从节点u到结点v的边权值为w
bool vis[maxn];
bool gift[maxn]; //gift[i]代表第i个节点是否为礼品节点
int num[maxn]; //num[i]代表以第i个节点为根的树中礼品节点的数量
/* DFS
* 1. 通过判断(u,v)和(v,x)两边是否相等,以及v是否为礼品节点,来判断节点u是否为礼品节点
* 2. 求以节点v为根节点的树中礼品节点的数量
*/
int dfs(int u, int v, int uv)
{
//叶子节点不是礼品节点
if(edge[v].size() == 1 && v != 1)
{
gift[v] = false;
return num[v] = 0;
}
int ans = 0;
for(int i = 0; i < edge[v].size(); i++)
{
int x = edge[v][i].first, vx = edge[v][i].second;
if(vis[x]) continue; //如果这个节点遍历过,跳过
vis[x] = true;
if(uv == vx) gift[u] = false; //如果从u开始的路径中相邻边颜色相同,则u不是礼品节点
ans += dfs(v, x, edge[v][i].second);
vis[x] = false;
}
if(!gift[v]) gift[u] = false; //如果v不是礼品节点,则u必不是礼品节点。(重点)
return num[v] = ans + gift[v];
}
int main()
{
freopen("in.txt", "r", stdin);
memset(vis, false, sizeof vis);
memset(gift, true, sizeof gift);
//1 输入树的边
scanf("%d", &n);
for(int i = 1, a, b, c; i < n; i++)
{
scanf("%d%d%d", &a, &b, &c);
edge[a].push_back(make_pair(b, c));
edge[b].push_back(make_pair(a, c));
}
//2 判断每个节点是否为礼品节点,同时求以每个节点为根节点的树中礼品节点的数量
vis[1] = true;
dfs(0, 1, 0); //实际是没有0->1这条边的,所以边权初始为0,防止和其他边相同对结果造成干扰
//3 输入R,并输出结果
scanf("%d", &q);
for(int i = 0; i < q; i++)
{
scanf("%d", &r);
printf("%d\n", num[r]);
}
return 0;
}