BZOJ 1086:[SCOI2005]王室联邦(DFS树分块)

http://www.lydsy.com/JudgeOnline/problem.php?id=1086

题意:给出n个点的树,让你对树进行分块,每块的大小范围在[b, 3b]之间。

思路:一开始想着维护一个sz[u]代表以u为根的子树(不包括u本身)的大小,如果在范围之内就分成一块,但是这样写感觉一些细节上的东西没考虑清楚,WA了很久。

看了别人的做法:http://blog.csdn.net/popoqqq/article/details/42772237,很简洁。

大概是维护一个栈,每次dfs结束的时候入栈,在每次遍历子树之前标记一下当前的栈顶,当遍历子树的时候,如果当前的栈顶 - 之前的栈顶 >= b的话,就可以分成一块了。最后剩下的元素和最后一块连通。

结果的正确性:因为之前的子树大小加起来小于b,当前的子树大小小于b,所以当前的块大小小于2*b。最后剩余的元素小于b,最后的块小于2*b,因此小于3*b。

因为b小于n的,所以可以保证一定有答案。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <vector>
 4 #include <algorithm>
 5 using namespace std;
 6 #define N 1010
 7 struct Edge {
 8     int v, nxt;
 9 } edge[N*2];
10 int n, b, head[N], tot, belong[N], stk[N], top;
11 vector<int> ans;
12 
13 void Add(int u, int v) {
14     edge[tot] = (Edge) {v, head[u]}; head[u] = tot++;
15     edge[tot] = (Edge) {u, head[v]}; head[v] = tot++;
16 }
17 
18 void dfs(int u, int fa) {
19     int base = top; // 之前的栈顶
20     for(int i = head[u]; ~i; i = edge[i].nxt) {
21         int v = edge[i].v;
22         if(v == fa) continue;
23         dfs(v, u);
24         if(top - base >= b) {
25             ans.push_back(u);
26             while(base < top) belong[stk[top--]] = ans.size();
27         }
28     }
29     stk[++top] = u;
30 }
31 
32 int main() {
33     while(~scanf("%d%d", &n, &b)) {
34         memset(head, -1, sizeof(head)); tot = top = 0;
35         memset(belong, -1, sizeof(belong));
36         for(int i = 1; i < n; i++) {
37             int u, v; scanf("%d%d", &u, &v);
38             Add(u, v);
39         }
40         dfs(1, -1);
41         while(top) belong[stk[top--]] = ans.size(); // 最后元素
42         printf("%d\n", ans.size());
43         for(int i = 1; i <= n; i++) printf("%d%c", belong[i], i == n ? '\n' : ' ');
44         for(int i = 0; i < ans.size(); i++) printf("%d%c", ans[i], ans.size() - 1 == i ? '\n' : ' ');
45     }
46     return 0;
47 }

 

posted @ 2017-02-23 14:15  Shadowdsp  阅读(...)  评论(... 编辑 收藏