zoukankan      html  css  js  c++  java
  • 点分治

    点分治

    这是一种统计树上所有路径的算法,分治时间复杂度nlogn

    首先对于本子树选择一个根(重心),然后统计所有经过根的路径,然后分治每个子树。

    有一个端点为根的路径和单个点的路径需要特殊统计。

    点分治不仅可以统计所有路径,还能把对路径的询问离线下来计算。

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

    算法框架大概是:

    1.找根

    2.进行统计,看情况需要1/2次DFS,还要记录一些东西。

    3.清空,对每个子树进行点分治。


    来两道例题:

    洛谷P2634 聪聪可可 求树上有多少边权和为3的倍数的路径。

    统计部分:

    对于一端为根的路径,假设先存在一个0子树即可。

    每次DFS一个子树,把路径%3统计起来。

    与前面子树的结果相乘相加,然后并入前面子树。

      1 #include <cstdio>
      2 #include <algorithm>
      3 #define say(a) printf(#a), printf(" = %d 
    ", 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 
    ", 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 点分治 + 线性基合并。

  • 相关阅读:
    Struts2总结
    自动数据表格JQuery
    OGNL表达式介绍
    Struts2重定向
    struts2异常记录--java.lang.IllegalStateException
    WEB相关文件的加载顺序
    js如何获取select下拉框的value以及文本内容
    小知识点
    DOM文档对象总结
    圆桌聚餐(cogs 729)
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/9682368.html
Copyright © 2011-2022 走看看