一点一点看,即时更新。

    首先是点的分治,如何点的分治是重点,若随机找点作为根,最坏情况可能导致复杂度退化成O(N),故要找一个点,论文说此点叫重心,使得分出来的子树中,尽量保持平衡,这样需要O(N)的时间来进行预处理找出重心,然后分治的时候复杂度就变成了O( logn )。

    POJ1655 Balancing Act

    http://acm.pku.edu.cn/JudgeOnline/problem?id=1655

    POJ3107 Godfather

    http://acm.pku.edu.cn/JudgeOnline/problem?id=3107

    POJ2378  Tree Cutting

    http://acm.pku.edu.cn/JudgeOnline/problem?id=2378

    就是解决这个找重心的问题,取子树中的最大值来作为balance量

    做法是可以任意选一点为根,然后DFS,以此根为一棵有根树,记录每个节点所在子树的节点数 sum[ i ] 。那么 balance[ i ]的值为子树中最大的 sum[ son(i) ]值,还要比较的还有n - sum[ i ],这个是由于断掉该点后,该点以上的树的节点数。

POJ 1655 

1 #include <iostream>
2 #include <vector>
3  using namespace std;
4
5  const int maxn = 20000 + 1;
6
7 vector<int> tt[maxn];
8  int n, balance[maxn], sum[maxn];
9 bool visit[maxn];
10
11 void init()
12 {
13 for(int i=0; i < n; i++)
14 {
15 visit[i] = 0;
16 sum[i] = 0;
17 balance[i] = 0;
18 tt[i].clear();
19 }
20 }
21
22 void DFS(int u)
23 {
24 visit[u] = 1;
25 int sz = tt[u].size();
26 for(int i=0; i < sz; i++)
27 {
28 int v = tt[u][i];
29 if(visit[v])
30 continue;
31 DFS(v);
32 sum[u] += sum[v];
33 balance[u] = max(sum[v] , balance[u]);
34 }
35 sum[u] ++;
36 }
37
38 void solve()
39 {
40 DFS(1);
41 for(int i=1; i <= n; i++)
42 {
43 balance[i] = max(n - sum[i] , balance[i]);
44 }
45 int mn = balance[1] , index = 1;
46 for(int i=2; i <= n; i++)
47 {
48 if(balance[i] < mn)
49 {
50 mn = balance[i];
51 index = i;
52 }
53 }
54 cout << index << ' ' << mn << endl;
55 }
56
57 int main()
58 {
59 freopen("in", "r", stdin);
60 int cas;
61 cin >> cas;
62 while(cas --)
63 {
64 cin >> n;
65 init();
66 int u , v;
67 for(int i=0; i < n - 1; i++)
68 {
69 cin >> u >> v;
70 tt[u].push_back(v);
71 tt[v].push_back(u);
72 }
73 solve();
74 }
75 return 0;
76 }
77

 

    接下来是分治,既每次找重心,此时递归深度变小,处理这棵树,然后就可以删除这个根,进行找子树的重心。虽然这样找重心后处理会可能出现找很多次重心,但由于递归深度变小了,所以每次找的时间是变小的。

    POJ 1741 Tree

    http://acm.pku.edu.cn/JudgeOnline/problem?id=1741

    这个我TLE无数次,其实做法是对的,只是由于在找根的时候,用了一个visit数组来标记访问过的点,所以每次要memset,导致TLE,其实可以直接找标志不要回去父亲节点即可。

    1、先DFS找出这棵树的重心,然后再一次DFS得到以这个重心为根的时候,各个点到达根的距离。

    2、然后求出距离当中两者之和 <= k的对数。

    3、这些对数之中,可能存在经过某个子树到根后,又返回来,这个时候需要DFS一遍,找出以这个子树为根的时候,各个点到达之前的根的距离。

    4、减去这些距离当中两者之和 <= k的对数。

    5、以这个重心为根,删掉这个重心,然后对子树进行处理,返回1。

    POJ 1987 Distance Statistics

    http://acm.pku.edu.cn/JudgeOnline/problem?id=1987

    同上题一模一样。

    POJ 2114 Boatherds

    http://acm.pku.edu.cn/JudgeOnline/problem?id=2114

    和上题基本一样,就是求权值刚好等于k的路径存不存在

POJ 1741 

1 #include <cstdio>
2 #include <algorithm>
3
4 using namespace std;
5
6 const int maxn = 20000 + 1;
7
8 struct node
9 {
10 int v , w;
11 int balance , sum , deepth;
12 node* next;
13 } *adj[maxn] , edge[maxn] , tt[maxn];
14
15 int n , k , ans , len, D[maxn], size[maxn], sign[maxn];
16 bool used[maxn];
17
18 int e_num;
19 void init()
20 {
21 e_num = ans = 0;
22 for(int i=1; i <= n; i++)
23 {
24 adj[i] = NULL;
25 used[i] = false;
26 }
27 }
28
29 void add_edge(int u , int v , int w)
30 {
31 node* ptr = &edge[ e_num ++ ] ;
32 ptr -> v = v;
33 ptr -> w = w;
34 ptr -> next = adj[u];
35 adj[u] = ptr;
36 }
37
38 int sz;
39 void DFS(int u, int f)
40 {
41 tt[u].sum = tt[u].balance = 0;
42 for(node* ptr = adj[u]; ptr; ptr = ptr -> next)
43 {
44 int v = ptr -> v;
45 if(v == f || used[v])
46 continue;
47 DFS(v , u);
48 tt[u].sum += tt[v].sum;
49 tt[u].balance = max(tt[u].balance , tt[v].sum);
50 }
51 tt[u].sum ++;
52 size[ sz ] = tt[u].balance;
53 sign[ sz ++ ] = u;
54 }
55
56 int GET_ROOT(int root)
57 {
58 sz = 0;
59 DFS(root , 0);
60 int mn = 0x7fffffff , total = tt[root].sum;
61 for(int i=0; i < sz; i++)
62 {
63 size[i] = max(size[i] , total - size[i]);
64 if(size[i] < mn)
65 {
66 mn = size[i];
67 root = sign[i];
68 }
69 }
70 return root;
71 }
72
73 void GET_DEEP(int u , int deep , int f)
74 {
75 D[len ++] = deep;
76 for(node* ptr = adj[u]; ptr; ptr = ptr -> next)
77 {
78 int v = ptr -> v;
79 int w = ptr -> w;
80 if(v == f || used[v])
81 continue;
82 GET_DEEP(v , w + deep , u);
83 }
84 }
85
86 void doit1(int root)
87 {
88 sort(D , D + len);
89 int l = 0 , r = len - 1;
90 while(l < r)
91 {
92 if(D[l] + D[r] <= k)
93 {
94 ans += (r - l);
95 l ++;
96 } else
97 r --;
98 }
99 }
100
101 void doit2(int root)
102 {
103 used[root] = true;
104 for(node* ptr = adj[root]; ptr; ptr = ptr -> next)
105 {
106 if(used[ptr -> v])
107 continue;
108 len = 0 ;
109 GET_DEEP(ptr -> v , ptr -> w , root);
110 sort(D , D + len);
111 int l = 0 , r = len - 1;
112 while(l < r)
113 {
114 if(D[l] + D[r] <= k)
115 {
116 ans -= (r - l);
117 l ++;
118 } else
119 r --;
120 }
121 }
122 }
123
124 void solve(int root)
125 {
126 root = GET_ROOT(root);
127 len = 0;
128 GET_DEEP(root , 0 , 0);
129 doit1(root);
130 doit2(root);
131 for(node* ptr = adj[root]; ptr; ptr = ptr -> next)
132 {
133 if(used[ptr -> v])
134 continue;
135 solve(ptr -> v);
136 }
137 }
138
139 int main()
140 {
141 while(scanf("%d %d", &n, &k) != EOF)
142 {
143 if(n == 0 && k == 0)
144 break;
145 init();
146 for(int i=1; i <= n - 1; i++)
147 {
148 int u , v , w;
149 scanf("%d %d %d", &u, &v, &w);
150 add_edge(u , v , w);
151 add_edge(v , u , w);
152 }
153 solve(1);
154 printf("%d\n", ans);
155 }
156 return 0;
157 }
158

 

posted on 2010-06-01 00:38  xIao.wU 思维磁场  阅读(1603)  评论(0编辑  收藏  举报