OSU! on tree

dsu on tree

好吧,這個毒瘤......算法

樹剖和啓發式合併的雜合體。ide

用於解決靜態子樹問題,複雜度O(nlogn * insert時間)函數

由於dsu是並查集的意思因此算法名字大概就是什麼樹上並查集之類的鬼東西。spa

由於dsu是並查集的意思因此函數名字看起來會很奇怪......code

主要思想是這樣的:blog

首先仿照樹剖搞出輕重子節點。繼承

dsu到一個點的時候,dsu全部的輕子樹並消除影響。string

dsu重子樹並保留影響,從重子節點那裏繼承答案。it

計算本身的貢獻。io

插入全部輕子樹並更新本身的答案。

若是本身是輕子樹的話,消除本身的影響。

回溯。

可知每一個點最多通過logn個重鏈就能到達根,因此每一個點最多插入logn次。


例題:

給你一棵樹以1號節點爲根的樹,每一個節點上有一個體積爲v,價值爲w的物品。現
在要你統計,對於全部點i,若是隻能取子樹i中的物品,則容積爲m的揹包

至多能裝總價值多少的物品。 n <= 50000 m <= 300

跟大部分dsu on tree有點區別,由於是樹形揹包變種因此不用消除輕子樹影響。

首先考慮正常揹包:

計算完子節點後merge子節點和本身,複雜度V²

總共nV²會超時。

而後考慮dsu on tree:

把重兒子memcpy給本身,而後依次insert每一個輕兒子,雖然看起來比以前那個慢可是實際上...

複雜度mnlogn,顯得十分之快...

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 const int N = 30010, M = 510;
 5 struct Edge {
 6     int v, nex;
 7 }edge[N]; int t;
 8 int e[N], son[N], siz[N];
 9 int f[N][M], cost[N], val[N], V;
10 
11 inline void add(int x, int y) {
12     t++;
13     edge[t].v = y;
14     edge[t].nex = e[x];
15     e[x] = t;
16     return;
17 }
18 
19 void DFS_1(int x) {
20     siz[x] = 1;
21     for(int i = e[x]; i; i = edge[i].nex) {
22         int y = edge[i].v;
23         DFS_1(y);
24         siz[x] += siz[y];
25         if(siz[y] > siz[son[x]]) {
26             son[x] = y;
27         }
28     }
29     return;
30 }
31 
32 void insert(int x, int p) {
33     for(int i = V; i >= cost[x]; i--) {
34         f[p][i] = std::max(f[p][i], f[p][i - cost[x]] + val[x]);
35     }
36     for(int i = e[x]; i; i = edge[i].nex) {
37         int y = edge[i].v;
38         insert(y, p);
39     }
40     return;
41 }
42 
43 void dsu(int x) {
44     for(int i = e[x]; i; i = edge[i].nex) {
45         int y = edge[i].v;
46         if(y == son[x]) {
47             continue;
48         }
49         dsu(y);
50     }
51     if(son[x]) {
52         dsu(son[x]);
53         memcpy(f[x], f[son[x]], sizeof(f[x]));
54     }
55     for(int i = V; i >= cost[x]; i--) {
56         f[x][i] = std::max(f[x][i], f[x][i - cost[x]] + val[x]);
57     }
58     for(int i = e[x]; i; i = edge[i].nex) {
59         int y = edge[i].v;
60         if(y == son[x]) {
61             continue;
62         }
63         insert(y, x);
64     }
65     return;
66 }
67 
68 int main() {
69     int n;
70     scanf("%d%d", &n, &V);
71     for(int i = 1; i <= n; i++) {
72         scanf("%d%d", &cost[i], &val[i]);
73     }
74     for(int i = 2, x; i <= n; i++) {
75         scanf("%d", &x);
76         add(x, i);
77     }
78     DFS_1(1);
79     dsu(1);
80     for(int i = 1; i <= n; i++) {
81         printf("%d ", f[i][V]);
82     }
83     return 0;
84 } 
AC代碼

CF 600E Lomsat gelral

題意:求樹上每一個子樹中出現次數最多的顏色。若是有相同次數就顏色相加。

套路:先走輕兒子,傳清空標記。

而後走重兒子,不清空。繼承答案。

統計本身的貢獻。

insert輕兒子並統計答案。

若是有清空標記就清空。

  1 #include <cstdio>
  2 const int N = 100010;
  3 typedef long long LL;
  4 struct Edge {
  5     int v, nex;
  6 }edge[N << 1]; int top;
  7 int e[N], val[N], bin[N], large[N], son[N], siz[N];
  8 LL ans[N];
  9 
 10 inline void add(int x, int y) {
 11     top++;
 12     edge[top].v = y;
 13     edge[top].nex = e[x];
 14     e[x] = top;
 15     return;
 16 }
 17 
 18 void DFS_1(int x, int f) {
 19     siz[x] = 1;
 20     for(int i = e[x]; i; i = edge[i].nex) {
 21         int y = edge[i].v;
 22         if(y == f) {
 23             continue;
 24         }
 25         DFS_1(y, x);
 26         siz[x] += siz[y];
 27         if(siz[y] > siz[son[x]]) {
 28             son[x] = y;
 29         }
 30     }
 31     return;
 32 }
 33 
 34 void insert(int x, int f, int p) {
 35     bin[val[x]]++;
 36     if(bin[val[x]] > large[p]) {
 37         large[p] = bin[val[x]];
 38         ans[p] = val[x];
 39     }
 40     else if(bin[val[x]] == large[p]) {
 41         ans[p] += val[x];
 42     }
 43 
 44     for(int i = e[x]; i; i = edge[i].nex) {
 45         int y = edge[i].v;
 46         if(y != f) {
 47             insert(y, x, p);
 48         }
 49     }
 50     return;
 51 }
 52 
 53 void erase(int x, int f) {
 54     bin[val[x]]--;
 55     for(int i = e[x]; i; i = edge[i].nex) {
 56         int y = edge[i].v;
 57         if(y != f) {
 58             erase(y, x);
 59         }
 60     }
 61     return;
 62 }
 63 
 64 void dsu(int x, int f, int k) {
 65     for(int i = e[x]; i; i = edge[i].nex) {
 66         int y = edge[i].v;
 67         if(y == f || y == son[x]) {
 68             continue;
 69         }
 70         dsu(y, x, 0);
 71     }
 72     if(son[x]) {
 73         dsu(son[x], x, 1);
 74         ans[x] = ans[son[x]];
 75         large[x] = large[son[x]];
 76     }
 77 
 78     bin[val[x]]++;
 79     if(bin[val[x]] > large[x]) {
 80         large[x] = bin[val[x]];
 81         ans[x] = val[x];
 82     }
 83     else if(bin[val[x]] == large[x]) {
 84         ans[x] += val[x];
 85     }
 86 
 87     for(int i = e[x]; i; i = edge[i].nex) {
 88         int y = edge[i].v;
 89         if(y == f || y == son[x]) {
 90             continue;
 91         }
 92         insert(y, x, x);
 93     }
 94     if(!k) {
 95         erase(x, f);
 96     }
 97     return;
 98 }
 99 
100 int main() {
101     int n;
102     scanf("%d", &n);
103     for(int i = 1; i <= n; i++) {
104         scanf("%d", &val[i]);
105     }
106     for(int i = 1, x, y; i < n; i++) {
107         scanf("%d%d", &x, &y);
108         add(x, y);
109         add(y, x);
110     }
111     DFS_1(1, 0);
112     dsu(1, 0, 1);
113     for(int i = 1; i <= n; i++) {
114         printf("%I64d ", ans[i]);
115     }
116     return 0;
117 }
AC代碼

一開始WA了第25個點,沒找出錯來,仔細思考發現答案多是n²級別的,爆int了,開long long以後A掉。

相關文章
相關標籤/搜索