Codeforces Round #781(C. Tree Infection)
A tree is a connected graph without cycles. A rooted tree has a special vertex called the root. The parent of a vertex vv (different from root) is the previous to vv vertex on the shortest path from the root to the vertex vv. Children of the vertex vv are all vertices for which vv is the parent.
You are given a rooted tree with nn vertices. The vertex 11 is the root. Initially, all vertices are healthy.
Each second you do two operations, the spreading operation and, after that, the injection operation:
- Spreading: for each vertex vv, if at least one child of vv is infected, you can spread the disease by infecting at most one other child of vv of your choice.
- Injection: you can choose any healthy vertex and infect it.
This process repeats each second until the whole tree is infected. You need to find the minimal number of seconds needed to infect the whole tree.
The input consists of multiple test cases. The first line contains a single integer tt (1≤t≤1e4) — the number of test cases. Description of the test cases follows.
The first line of each test case contains a single integer nn (2≤n≤2e5) — the number of the vertices in the given tree.
The second line of each test case contains n−1n−1 integers p2,p3,…,pnp2,p3,…,pn (1≤pi≤n), where pipi is the ancestor of the ii-th vertex in the tree.
It is guaranteed that the given graph is a tree.
It is guaranteed that the sum of nn over all test cases doesn't exceed 2e5.
1 # include<iostream>
2 # include<algorithm>
3 # include<queue>
4 # define int long long
5 # define endl "\n"
6 using namespace std;
7 const int N = 1000050;
8 int f[N],vis[N];
9
10 void solve(){
11 int n;
12 cin>>n;
13 for(int i = 1;i <= n;++i) f[i] = 0;
14 for(int i = 2;i <= n;++i){
15 int x;
16 cin>>x;
17 f[x]++;
18 }
19 int l = 1,r = n,res = n;
20 while(l <= r){
21 int mid = (l+r)>>1;
22 for(int i = 0;i <= n;++i) vis[i] = 0;
23 priority_queue<pair<int,int> > q;
24
25 for(int i = 1;i <= n;++i) if(f[i]) q.push({f[i],i});
26 q.push({1,0});
27 for(int i = 1;i <= mid;++i){
28 if(q.empty()) break;
29 auto now = q.top();q.pop();
30 if(!vis[now.second]) now.first -=mid - i+1,vis[now.second] = 1;
31 else now.first--;
32 if(now.first > 0) q.push(now);
33 }
34 if(q.empty()) res = mid,r = mid-1;
35 else l = mid+1;
36 }
37 cout<<res<<endl;
38 }
39
40 int tt;
41 signed main(){
42 ios::sync_with_stdio(false);
43 cin.tie(nullptr);
44 cout.tie(nullptr);
45 cin>>tt;
46 while(tt--){
47 solve();
48 }
49
50
51 return 0;
52 }
二分实在是精彩
根据题意将相同父节的个数进排序,每次对个数最多的进行操作
如果尚未被感染则选择注射,如果已经被注射过,则选择传播给相同父节点的子节点
通过priority_queue来进行排序,每次取对头进行操作,通过vis进行对节点(相同父节点)的判断是否被感染过
而二分的是操作次数(二分答案),有两种可能,如果操作次数内使得队列为空说明在当前操作次数可以满足全部感染,继续查找更小的操作次数
否则就不能满足全部感染,查找更大的操作次数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】