點分治

點分治

這是一種統計樹上全部路徑的算法,分治時間複雜度nlognnode

首先對於本子樹選擇一個根(重心),而後統計全部通過根的路徑,而後分治每一個子樹。算法

有一個端點爲根的路徑和單個點的路徑須要特殊統計。數組

點分治不只能夠統計全部路徑,還能把對路徑的詢問離線下來計算。框架

注意div分治的時候siz要重置。ide

算法框架大概是:spa

1.找根指針

2.進行統計,看狀況須要1/2次DFS,還要記錄一些東西。code

3.清空,對每一個子樹進行點分治。blog


來兩道例題:排序

洛谷P2634 聰聰可可 求樹上有多少邊權和爲3的倍數的路徑。

統計部分:

對於一端爲根的路徑,假設先存在一個0子樹便可。

每次DFS一個子樹,把路徑%3統計起來。

與前面子樹的結果相乘相加,而後併入前面子樹。

  1 #include <cstdio>
  2 #include <algorithm>
  3 #define say(a) printf(#a), printf(" = %d \n", a)
  4 const int N = 20010, INF = 0x3f3f3f3f;
  5 
  6 struct Edge {
  7     int nex, v, len;
  8 }edge[N << 1]; int top;
  9 
 10 int e[N], d[N], fr[N], siz[N], fa[N];
 11 bool del[N];
 12 int n_, root_, small, ans, sum[3], sum_[3];
 13 
 14 inline void add(int x, int y, int z) {
 15     edge[++top].v = y;
 16     edge[top].len = z;
 17     edge[top].nex = e[x];
 18     e[x] = top;
 19     return;
 20 }
 21 
 22 int getroot(int x, int f) {
 23     siz[x] = 1;
 24     int large = 0;
 25     for(int i = e[x]; i; i = edge[i].nex) {
 26         int y = edge[i].v;
 27         if(y == f || del[y]) {
 28             continue;
 29         }
 30         int temp = getroot(y, x);
 31         siz[x] += temp;
 32         large = std::max(large, temp);
 33     }
 34     if(std::max(large, n_ - siz[x]) < small) {
 35         small = std::max(large, n_ - siz[x]);
 36         root_ = x;
 37     }
 38     return siz[x];
 39 }
 40 
 41 void DFS_1(int x, int f) {
 42     fa[x] = f;
 43     for(int i = e[x]; i; i = edge[i].nex) {
 44         int y = edge[i].v;
 45         if(y == fa[x] || del[y]) {
 46             continue;
 47         }
 48         d[y] = d[x] + edge[i].len;
 49         sum_[d[y] % 3]++;
 50         //fr[y] = fr[x];
 51         DFS_1(y, x);
 52     }
 53     return;
 54 }
 55 
 56 void poi_div(int root) {
 57     sum[0] = sum[1] = sum[2] = 0;
 58     sum[0] = 1;
 59     small = INF;
 60     getroot(root, 0);
 61     root = root_;
 62     for(int i = e[root]; i; i = edge[i].nex) {
 63         int y = edge[i].v;
 64         if(del[y]) {
 65             continue;
 66         }
 67         d[y] = edge[i].len;
 68         sum_[d[y] % 3]++;
 69         fr[y] = y;
 70         DFS_1(y, root); /// get d[], fa[]
 71         ans += sum_[0] * sum[0];
 72         ans += sum_[1] * sum[2];
 73         ans += sum_[2] * sum[1];
 74 
 75         for(int i = 0; i < 3; i++) {
 76             sum[i] += sum_[i];
 77             sum_[i] = 0;
 78         }
 79     }
 80     del[root] = 1;
 81     for(int i = e[root]; i; i = edge[i].nex) {
 82         int y = edge[i].v;
 83         if(del[y]) {
 84             continue;
 85         }
 86         n_ = siz[y];
 87         poi_div(y);
 88     }
 89     return;
 90 }
 91 
 92 int gcd(int x, int y) {
 93     if(!y) {
 94         return x;
 95     }
 96     return gcd(y, x % y);
 97 }
 98 
 99 int main() {
100     int n;
101     scanf("%d", &n);
102     for(int i = 1, x, y, z; i < n; i++) {
103         scanf("%d%d%d", &x, &y, &z);
104         add(x, y, z);
105         add(y, x, z);
106     }
107     n_ = n;
108     poi_div(1);
109 
110     ans <<= 1;
111     ans += n;
112     int g = gcd(ans, n * n);
113     printf("%d/%d", ans / g, n * n / g);
114 
115     return 0;
116 }
AC代碼

洛谷P4178 Tree 求樹上有多少邊權和不超過k的路徑

這個是另外一種統計方法:

首先掃描每個子樹,得出深度d和屬於哪一個子樹fr

把全部子節點放入數組t中,按照d排序。

使用兩個指針l = 1,r = size(t)

在t[l + 1, r]裏有cnt[i]個i子樹中的節點。

而後每次把l++,調整r

以t[l]爲一個端點且t[l]的d較小的路徑個數就是r - l - cnt[fr[t[l]]]

最後記得清空t和cnt

  1 #include <cstdio>
  2 #include <algorithm>
  3 #include <cstring>
  4 #define say(a) printf(#a), printf(" = %d \n", a)
  5 typedef long long LL;
  6 
  7 const int N = 40005, INF = 0x3f3f3f3f;
  8 
  9 struct Edge {
 10     int v, nex, len;
 11 }edge[N << 1]; int top;
 12 
 13 int e[N], fa[N], fr[N], siz[N], cnt[N], t[N];
 14 bool del[N];
 15 int n_, root_, small, tt, ans;
 16 LL d[N], k;
 17 
 18 inline bool cmp(int x, int y) {
 19     return d[x] < d[y];
 20 }
 21 
 22 inline void add(int x, int y, int z) {
 23     edge[++top].v = y;
 24     edge[top].nex = e[x];
 25     edge[top].len = z;
 26     e[x] = top;
 27     return;
 28 }
 29 
 30 int getroot(int x, int f) {
 31     siz[x] = 1;
 32     int large = 0;
 33     for(int i = e[x]; i; i = edge[i].nex) {
 34         int y = edge[i].v;
 35         if(y == f || del[y]) {
 36             continue;
 37         }
 38         int temp = getroot(y, x);
 39         siz[x] += temp;
 40         large = std::max(large, temp);
 41     }
 42     if(std::max(large, n_ - siz[x]) < small) {
 43         small = std::max(large, n_ - siz[x]);
 44         root_ = x;
 45     }
 46     return siz[x];
 47 }
 48 
 49 void DFS_1(int x, int f) {
 50     fa[x] = f;
 51     t[++tt] = x;
 52     cnt[fr[x]]++;
 53     for(int i = e[x]; i; i = edge[i].nex) {
 54         int y = edge[i].v;
 55         if(y == fa[x] || del[y]) {
 56             continue;
 57         }
 58         fr[y] = fr[x];
 59         d[y] = d[x] + edge[i].len;
 60         ans += (d[y] <= k);
 61         DFS_1(y, x);
 62     }
 63     return;
 64 }
 65 
 66 void poi_div(int x) {
 67     small = INF;
 68     getroot(x, 0);
 69     x = root_;
 70     for(int i = e[x]; i; i = edge[i].nex) {
 71         int y = edge[i].v;
 72         if(del[y]) {
 73             continue;
 74         }
 75         fr[y] = y;
 76         d[y] = edge[i].len;
 77         ans += (d[y] <= k); /// way that one point is root
 78         DFS_1(y, x); /// get d[] fa[] fr[]   insert t[]
 79     }
 80     //cal
 81     std::sort(t + 1, t + tt + 1, cmp);
 82     int l = 1, r = tt; /// cnt[]  [l + 1, r]
 83     cnt[fr[t[1]]]--;
 84     while(l < r) {
 85         while(l < r && d[t[l]] + d[t[r]] > k) {
 86             cnt[fr[t[r]]]--;
 87             r--;
 88         }
 89         if(l == r) {
 90             break;
 91         }
 92         ans += (r - l - cnt[fr[t[l]]]); /// cal 
 93         l++;
 94         cnt[fr[t[l]]]--;
 95     }
 96     cnt[fr[t[l]]] = 0; /// here !! 
 97     /// pay attentions !! above may error
 98     tt = 0;
 99     del[x] = 1;
100     for(int i = e[x]; i; i = edge[i].nex) {
101         int y = edge[i].v;
102         if(del[y]) {
103             continue;
104         }
105         n_ = siz[y];
106         poi_div(y);
107     }
108 
109     return;
110 }
111 
112 int main() {
113     int n;
114     scanf("%d", &n);
115     for(int i = 1, x, y, z; i < n; i++) {
116         scanf("%d%d%d", &x, &y, &z);
117         add(x, y, z);
118         add(y, x, z);
119     }
120     scanf("%lld", &k);
121 
122     n_ = n;
123     poi_div(1);
124 
125     printf("%d", ans);
126 
127     return 0;
128 }
AC代碼

洛谷P3806 【模板】點分治1

求100次樹上是否存在長度爲K的路徑

我一開始想的是離線,由於感受上跑100次點分治確定會超時。

結果還真是求100次...

每次點分治的時候開一個set,而後看set裏面有沒有

好,點分治的套路大概就這樣。

 


又來了一道題......考場上沒想到怎麼統計答案就寫的暴力。

題意:給你個樹,每條邊有長度和權值。求長度不超過L的鏈的最大權值和。n<=30000

一眼點分治......

統計答案是這樣的:搞個set,而後每一個子樹的全部點都提取出來搞個結構體數組,而後排序。

發現對於len單增的那個子樹內的節點,set裏的決策集合是單增的。

因而維護該決策集合內的權值最大值便可。

反正就是那樣......看代碼。

  1 #include <cstdio>
  2 #include <set>
  3 #include <algorithm>
  4 
  5 const int N = 30010, INF = 0x3f3f3f3f;
  6 
  7 struct Edge {
  8     int nex, v;
  9     int len, val;
 10 }edge[N << 1]; int top;
 11 
 12 struct Node {
 13     int d, val;
 14     Node(int dis = 0, int value = 0) {
 15         d = dis;
 16         val = value;
 17     }
 18     inline bool operator <(const Node &w) const {
 19         return d < w.d;
 20     }
 21 }node[N]; int t;
 22 
 23 int L, ans;
 24 int siz[N], num, small, root; // point divided
 25 int e[N];
 26 bool del[N];
 27 
 28 std::set<Node> st;
 29 
 30 inline bool cmp(const Node &a, const Node &b) {
 31     return a.d > b.d;
 32 }
 33 
 34 inline void add(int x, int y, int z, int w) {
 35     top++;
 36     edge[top].v = y;
 37     edge[top].len = z;
 38     edge[top].val = w;
 39     edge[top].nex = e[x];
 40     e[x] = top;
 41     return;
 42 }
 43 
 44 void getroot(int x, int f) {
 45     siz[x] = 1;
 46     int large = -1;
 47     for(int i = e[x]; i; i = edge[i].nex) {
 48         int y = edge[i].v;
 49         if(y == f || del[y]) {
 50             continue;
 51         }
 52         getroot(y, x);
 53         large = std::max(large, siz[y]);
 54         siz[x] += siz[y];
 55     }
 56     large = std::max(large, num - siz[x]);
 57     if(large < small) {
 58         small = large;
 59         root = x;
 60     }
 61     return;
 62 }
 63 
 64 void DFS_1(int x, int f, int s, int v) {
 65     if(s > L) {
 66         return;
 67     }
 68     ans = std::max(ans, v);
 69     node[++t] = Node(s, v);
 70     for(int i = e[x]; i; i = edge[i].nex) {
 71         int y = edge[i].v;
 72         if(del[y] || y == f) {
 73             continue;
 74         }
 75         DFS_1(y, x, s + edge[i].len, v + edge[i].val);
 76     }
 77     return;
 78 }
 79 
 80 void poi_div(int x) {
 81     small = INF;
 82     getroot(x, 0);
 83     x = root;
 84 
 85     st.clear();
 86     for(int i = e[x]; i; i = edge[i].nex) {
 87         int y = edge[i].v;
 88         if(del[y]) {
 89             continue;
 90         }
 91 
 92         t = 0;
 93         DFS_1(y, x, edge[i].len, edge[i].val);
 94 
 95         // cal ans
 96         std::sort(node + 1, node + t + 1, cmp);
 97         std::set<Node>::iterator it = st.begin();
 98         int large = 0; // error  -1
 99         for(int i = 1; i <= t; i++) {
100             while(it != st.end() && (*it).d + node[i].d <= L) {
101                 large = std::max(large, (*it).val);
102                 it++;
103             }
104             ans = std::max(ans, large + node[i].val);
105         }
106 
107         //merge  y
108         for(int i = 1; i <= t; i++) {
109             st.insert(node[i]);
110         }
111     }
112 
113     del[x] = 1;
114     for(int i = e[x]; i; i = edge[i].nex) {
115         int y = edge[i].v;
116         if(del[y]) {
117             continue;
118         }
119         num = siz[y];
120         poi_div(y);
121     }
122     return;
123 }
124 
125 int main() {
126     int n, m;
127     scanf("%d%d%d", &n, &m, &L);
128     for(int i = 1, x, y, z, w; i <= m; i++) {
129         scanf("%d%d%d%d", &x, &y, &z, &w);
130         add(x, y, z, w);
131         add(y, x, z, w);
132     }
133 
134     num = n;
135     poi_div(1);
136 
137     printf("%d", ans);
138     return 0;
139 }
AC代碼

 


loj#2013 點分治 + 線性基合併。

相關文章
相關標籤/搜索