Tree Problem

2024 CCPC Zhengzhou Invitational Contest K

Problem K. 树上问题

378QAQ 有一棵由 $n$ 个节点组成的无根树,节点编号从 1 到 $n$,每个节点有一个正整数点权。 378QAQ 认为一个节点是美丽节点,当且仅当该节点作为根时,对于除根节点以外的所有节点,其点权都不小于其父亲节点的点权的 $\frac{1}{2}$。 请你计算出有多少个节点是美丽节点。

Input

本题测试点包含多组数据。 第一行包含一个正整数 $t$($1 \leq t \leq 10^4$),表示数据组数。 对于每组数据: 第一行包含一个正整数 $n$($1 \leq n \leq 10^5$),表示节点数量。 第二行包含 $n$ 个正整数 $a_1, a_2, \ldots, a_n$($1 \leq a_i \leq 10^6$),表示编号为 $i$ 的节点点权。 之后 $n - 1$ 行,每行包含两个正整数 $u, v$($1 \leq u, v \leq n$, $u \neq v$),表示无根树中存在一条连接节点 $u$ 和节点 $v$ 的边。 保证单个测试点中所有数据的 $\sum n \leq 10^5$。

Output

对于每组数据,输出一个非负整数,代表美丽节点的数量。

Solving

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
const int M = 1e6+100; 
struct Edge
{
    int to,next;
};
// Tarjan 算法来寻找有向图中的强连通分量,并进行缩点。
struct SCC { ... ... };//  SCC中边的数量初始化的问题 !!
void solve()
{
    int n;
    cin >> n;
    SCC scc(n);
    vector<int> val(n+1);
    for(int i=1;i<=n;i++) cin >> val[i];
    for(int i=1;i<=n-1;i++)
    {
        int u,v;
        cin >> u >> v;
        if(2 * val[u] >=  val[v]) // 如果满足题意可以建边则建有向边
        {
            scc.addEdge(v,u);
        }
        if(2 * val[v] >= val[u])
        {
            scc.addEdge(u,v);
        }
    } 
    // 易得同一个强连通分量中的各个相邻点可以互为父亲,所以可以进行缩点
    vector<int> comp = scc.work();
    unordered_map<int,int> mp;
    for(int i=1;i<=n;i++) // 记录每个强连通分量里的原始点数目
    {
        mp[comp[i]]++;
    }
    vector<int> ind(scc.cnt,0);
    // 对缩点后的DAG建边求出入度
    for(int u=1;u<=n;u++)
    {   
        for(int i=scc.head[u];i;i=scc.edge[i].next)
        {
            int v = scc.edge[i].to;
            int cu = comp[u];
            int cv = comp[v];
            if(cu != cv) ind[cv]++;
        }
    }
    // 若美丽节点存在,缩点后的DAG一定是全联通的,即存在一个强连通分量为DAG的父亲节点
    // 其余均为子节点,正式的来说,即DAG中仅存在一点入度为0,其余入度均为1
    // 答案即为作为父亲强连通分量节点中原始节点的数目
    int zerocount = count(ind.begin()+1,ind.end(),0);
    if(zerocount>1) // 若不满足只有一个强连通分量为根节点 答案为0
    {
        cout << 0 << endl;
        return;
    }
    for(int i=1;i<scc.cnt;i++)
    {
        if(ind[i]==0)
        {	
            // 否则找到入度为0的强连通分量点,输出答案
            cout << mp[i] << endl; 
            break;
        }
    }
}
Licensed under CC BY-NC-SA 4.0
Member of the Qilu University Of Technology ACM-ICPC Association
Built with Hugo
Theme Stack designed by Jimmy