51nod1307(暴力树剖/二分&dfs/并查集)

题目链接: http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1307

题意: 中文题诶~

思路:

解法1:暴力树剖

用一个数组 num[i] 维护编号为 i 的边当前最大能承受的重量. 在加边的过程中根据给出的父亲节点将当前边所在的链上所有边的num都减去当前加的边的重量, 注意当前边也要减自重. 那么当num首次出现负数时加的边号即位答案;

事实上这个算法的时间复杂度是O(n^2)的, 不过本题并没有出那种退化成单链的数据, 所以直接暴力也能水过;

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 using namespace std;
 4
 5 const int MAXN = 5e4 + 10;
 6 struct node{
 7     int c, w, pre;
 8 }gel[MAXN];
 9 int num[MAXN];//num[i]为编号为i的绳子当前可以承受的最大重量
10
11 int main(void){
12     int n, ans = -1;
13     scanf("%d", &n);
14     for(int i = 0; i < n; i++){
15         scanf("%d%d%d", &gel[i].c, &gel[i].w, &gel[i].pre);
16         if(ans != -1) continue;
17         num[i] = gel[i].c;
18         int cnt = i;
19         while(cnt != -1){
20             num[cnt] -= gel[i].w;
21             if(ans == -1 && num[cnt] <= -1) ans = i;
22             cnt = gel[cnt].pre;//指向cnt的父亲节点
23         }
24     }
25     if(ans == -1) cout << n << endl;
26     else cout << ans << endl;
27     return 0;
28 }

解法2: 二分 + dfs

很显然在加边的过程中所有边的承受重量都是单调不减的, 那么可以考虑二分答案. 不过要注意判断函数的写法, 每一次判断都需要判断当前 mid条 边组成的树的所有边, 而不能只判断当前 mid 所在链上的边, 显然其他链上也可能存在不合法的边. 判断所有边的话可以 dfs 一遍, 回溯时判断即可.

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <vector>
 4 #define ll long long
 5 using namespace std;
 6
 7 const int MAXN = 5e4 + 10;
 8 struct node{
 9     int c, w, pre;
10 }gel[MAXN];
11 vector<int> sol[MAXN];
12 bool flag;
13
14 ll dfs(int u, int x){
15     ll sum = gel[u].w;
16     if(u > x) return 0;//mid边后面的不要算上去
17     for(int i = 0; i < sol[u].size(); i++){
18         sum += dfs(sol[u][i], x);
19     }
20     if(sum > gel[u].c && u) flag = false;//0是一个虚根,并没有对应的边
21     return sum;
22 }
23
24 int main(void){
25     int n;
26     scanf("%d", &n);
27     for(int i = 1; i <= n; i++){
28         scanf("%d%d%d", &gel[i].c, &gel[i].w, &gel[i].pre);
29         gel[i].pre++;
30         sol[gel[i].pre].push_back(i);
31     }
32     int l = 1, r = n, cnt = n;
33     while(l <= r){
34         flag = true;
35         int mid = (l + r) >> 1;
36         dfs(0, mid);
37         if(flag) cnt = mid, l = mid + 1;
38         else r = mid - 1;
39     }
40     printf("%d\n", cnt);
41 }

解法3: 并查集

记录每个节点的父节点

然后按输入顺序的倒叙 遍历每一个节点

计算以当前节点为根的子树的重量 ( 因为按照题目的输入顺序来说  当前节点要么没有子节点  要么子树已经遍历完 算入当前树的重量)

当遍历到某个节点时

当前节点与父节点的边无法承载当前节点为根的子树  便从输入序列最晚输入的节点开始删除

直到与父节点的边的能够承载当前节点为根的子树

又或者已经把遍历过的点都删除完了

这个过程中  用并查集维护某个节点 属于那一个跟节点 并且不断的压缩路径

每个条路径被压缩一次 均摊时间 就是边的数量 所以 这种做法很稳定的 O(n)

上面这段话是直接从讨论中复制过来的

代码:

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <vector>
 4 #define ll long long
 5 using namespace std;
 6
 7 const int MAXN = 1e5 + 10;
 8 struct node{
 9     ll c, w, p;
10 }gel[MAXN];
11
12 ll ww[MAXN];
13 vector<int> vt[MAXN];
14 int pre[MAXN], sol;
15
16 int find(int x){
17     return pre[x] == x ? x : pre[x] = find(pre[x]);
18 }
19
20 void update(int u){
21     for(int i = 0; i < vt[u].size(); i++){
22         gel[u].w += gel[vt[u][i]].w;
23         pre[vt[u][i]] = u;
24     }
25     while(gel[u].w > gel[u].c){//u即为当前根节点
26         gel[find(sol)].w -= ww[sol];
27         sol--;
28     }
29 }
30
31 int main(void){
32     int n;
33     scanf("%d", &n);
34     for(int i = 1; i <= n; i++){
35         scanf("%lld%lld%lld", &gel[i].c, &gel[i].w, &gel[i].p);
36         gel[i].p++;
37         vt[gel[i].p].push_back(i);
38         ww[i] = gel[i].w;//后面会对gel操作,所以需要先记录下gel的初始值来
39         pre[i] = i;
40     }
41     sol = n;
42     for(int i = n; i > 0; i--){
43         update(i);
44     }
45     printf("%d\n", sol);
46     return 0;
47 }

时间: 2024-10-11 01:18:33

51nod1307(暴力树剖/二分&dfs/并查集)的相关文章

DFS/并查集 Codeforces Round #286 (Div. 2) B - Mr. Kitayuta&#39;s Colorful Graph

题目传送门 1 /* 2 题意:两点之间有不同颜色的线连通,问两点间单一颜色连通的路径有几条 3 DFS:暴力每个颜色,以u走到v为结束标志,累加条数 4 注意:无向图 5 */ 6 #include <cstdio> 7 #include <iostream> 8 #include <algorithm> 9 #include <cstring> 10 #include <string> 11 #include <vector> 1

【bzoj3007】拯救小云公主 二分+对偶图+并查集

题目描述 英雄又即将踏上拯救公主的道路…… 这次的拯救目标是——爱和正义的小云公主. 英雄来到boss的洞穴门口,他一下子就懵了,因为面前不只是一只boss,而是上千只boss.当英雄意识到自己还是等级1的时候,他明白这就是一个不可能完成的任务. 但他不死心,他在想,能不能避开boss去拯救公主呢,嘻嘻. Boss的洞穴可以看成一个矩形,英雄在左下角(1,1),公主在右上角(row,line).英雄为了避开boss,当然是离boss距离越远越好了,所以英雄决定找一条路径使到距离boss的最短距离

CF 115 A 【求树最大深度/DFS/并查集】

CF A. Party time limit per test3 seconds memory limit per test256 megabytes inputstandard input outputstandard output A company has n employees numbered from 1 to n. Each employee either has no immediate manager or exactly one immediate manager, who

hdu 3081 【二分匹配+并查集+删边||最大路+并查集+二分枚举】

Marriage Match II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2307    Accepted Submission(s): 792 Problem Description Presumably, you all have known the question of stable marriage match. A

Codeforces 1140F Extending Set of Points 线段树 + 按秩合并并查集 (看题解)

Extending Set of Points 我们能发现, 如果把x轴y轴看成点, 那么答案就是在各个连通块里面的x轴的个数乘以y轴的个数之和. 然后就变成了一个并查集的问题, 但是这个题目里面有撤销的操作, 所以我们要把加入和撤销操作变成 这个点影响(L , R)之间的询问, 然后把它丢到线段树里面分成log段, 然后我们dfs一遍线段树, 用按秩合并并查集取维护, 回溯的时候将并查集撤销. #include<bits/stdc++.h> #define LL long long #def

HDU-3081-Marriage Match 2(最大流, 二分答案, 并查集)

链接: https://vjudge.net/problem/HDU-3081 题意: Presumably, you all have known the question of stable marriage match. A girl will choose a boy; it is similar as the game of playing house we used to play when we are kids. What a happy time as so many frie

1013 Battle Over Cities (25分) DFS | 并查集

1013 Battle Over Cities (25分) It is vitally important to have all the cities connected by highways in a war. If a city is occupied by the enemy, all the highways from/toward that city are closed. We must know immediately if we need to repair any othe

HDU3081Marriage Match II(二分答案+并查集+最大流SAP)经典

Marriage Match II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2507    Accepted Submission(s): 856 Problem Description Presumably, you all have known the question of stable marriage match. A

分珠(dfs+并查集)

1140 分珠 时间限制:500MS  内存限制:65536K提交次数:24 通过次数:18 题型: 编程题   语言: G++;GCC Description 如下图所示,有若干珠子,每颗珠子重量不同,珠子之间有一些细线将它们连在一起.现要求切断一些细线,将它们分成两部分, 分割后,单独每一部分的珠子仍保持相连,且要求尽量做到两部分总重相等或相差最少. 请编一程序,给定珠子个数.每颗珠子的重量以及珠子之间的连接情况,输出按上述要求分割后两部分总重的差值的绝对值. 输入格式 第一行有两个数N与M