点分治(树分治)

思想

点分治就是采用分治的思想递归处理子树,用容斥去除重复的贡献。

1、找到树的重心。

2、从当前树的重心开始,加上经过该重心的贡献,减去在同一棵子树的贡献,再对每一棵子树,重新寻找树的重心,重复2.

时间复杂度:O(nlogn*计算贡献时间)

题目

1、Tree POJ - 1741

  题意:有一棵树,每条边有个权值。问树上有多少点对(u,v)满足u到v的路径上所有边的边权之和不大于k.

  思路:点分治。对于当前根,点对要么经过该根,要么不经过该根,后者也意味着该点对位于同一棵子树中,递归后又回到前者。因此,关键是计算经过该根的点对。计算子树每个点到该根的距离,排序后二分寻找符合要求的点对数目(如果dis[L]+dis[R]<=k,则有R-L对符合条件),由于之后我们会处理子树,因此,当前计算的值包括了在同一子树的点对,需要将其去掉(因为之后递归处理子树会重新计算)。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn = 10000 + 10;
  6 const int INF = 0x3f3f3f3f;
  7 struct EDGE
  8 {
  9     int from, to, w, next;
 10     EDGE(int ff = 0, int tt = 0, int ww = 0, int nn = 0) :from(ff), to(tt), w(ww), next(nn) {}
 11 }edge[maxn << 1];
 12 int Head[maxn], totedge;
 13 void addedge(int from, int to, int w)
 14 {
 15     edge[totedge] = EDGE(from, to, w, Head[from]);
 16     Head[from]=totedge++;
 17     edge[totedge] = EDGE(to, from, w, Head[to]);
 18     Head[to]=totedge++;
 19 }
 20 
 21 int root, MX;//树的重心、找到的最大子树的最小结点数目
 22 int nums[maxn];//i为根,子树的结点数量(包括自己)
 23 int mxson[maxn];//i为根,最大的子树
 24 bool vis[maxn];
 25 int n, ans,k;//输入的树的大小、结果、指定的路径长度的最大值
 26 int sn;//当前根下,总树的大小
 27 int cnt;//记录当前得到的距离数目
 28 void init()
 29 {
 30     memset(Head, -1, sizeof(Head));
 31     totedge = 0;
 32     memset(vis, 0, sizeof(vis));
 33     MX = INF;
 34     ans = 0;
 35     sn = n;
 36 }
 37 void getRoot(int u, int fa)
 38 {//找到树的重心
 39     nums[u] = 1, mxson[u] = 0;
 40     for (int i = Head[u]; i != -1; i = edge[i].next)
 41     {
 42         int v = edge[i].to;
 43         if (vis[v] || v == fa)continue;
 44         getRoot(v, u);
 45         nums[u] += nums[v];
 46         mxson[u] = max(mxson[u], nums[v]);
 47     }
 48     mxson[u] = max(mxson[u], sn - nums[u]);// n - nums[u]是经过父亲节点的子树大小
 49     if (mxson[u] < MX) root = u, MX = mxson[u];
 50 
 51 }
 52 int dis[maxn];
 53 void getdis(int u, int fa, int dist)
 54 {//找到子树所有结点到当前根的距离
 55     dis[++cnt] = dist;
 56     for (int i = Head[u]; i != -1; i = edge[i].next)
 57     {
 58         int v = edge[i].to;
 59         if (vis[v] || v == fa) continue;
 60         getdis(v, u, dist + edge[i].w);
 61     }
 62 }
 63 int solve(int r, int len)
 64 {
 65     cnt = 0;
 66     memset(dis, 0, sizeof(dis));
 67     getdis(r, 0, len);
 68     sort(dis + 1, dis + 1 + cnt);
 69     int L = 1, R = cnt;
 70     int tans = 0;
 71     while (L <= R)
 72     {
 73         if (dis[R] + dis[L] <= k)
 74         {
 75             tans +=R - L;//一共有R-L对满足条件(L,L+1)、(L,L+2)……(L,L+R)
 76             L++;
 77         }
 78         else R--;
 79     }
 80     return tans;
 81 
 82 }
 83 void pointDivide(int tr)
 84 {
 85     ans += solve(tr, 0);//求解经过tr的所有路径(点对)
 86     vis[tr] = true;//当前点标记
 87     for (int i = Head[tr]; i != -1; i = edge[i].next)
 88     {//分治处理每一棵子树
 89         int v = edge[i].to;
 90         if (vis[v]) continue;
 91         ans -= solve(v, edge[i].w);//容斥去除重复部分
 92         sn = nums[v];//重设当前总树大小,寻找新的分治点(重心)
 93         root = 0;
 94         MX = INF;
 95         getRoot(v, 0);
 96         pointDivide(root); //递归处理新的分治点
 97     }
 98 }
 99 
100 int main()
101 {
102     while (~scanf("%d%d", &n, &k)&&n!=0&&k!=0)
103     {
104         init();
105         for (int i = 1; i <= n - 1; i++)
106         {
107             int u, v, w;
108             scanf("%d%d%d", &u, &v, &w);
109             addedge(u, v, w);
110         }
111         getRoot(1, 0);
112         pointDivide(root);
113         printf("%d\n", ans);
114     }
115     return 0;
116 }
View Code

2、聪聪可可 HYSBZ - 2152

  题意:求树上点对之间距离为3的倍数的个数。(u,v)与(v,u)算不同点对。

  思路:点分治,每次计算子树各个结点到根的距离%3,统计%3结果为0、1、2的数目,则符合条件的点对数目为dis[0]*(dis[0]-1)+dis[0]+dis[1]*dis[2]*2。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 using namespace std;
  6 const int maxn = 20000 + 10;
  7 const int INF = 0x3f3f3f3f;
  8 struct EDGE
  9 {
 10     int from, to, w, next;
 11     EDGE(int ff = 0, int tt = 0, int ww = 0, int nn = 0) :from(ff), to(tt), w(ww), next(nn) {}
 12 }edge[maxn << 1];
 13 int Head[maxn], totedge;
 14 int gcd(int x, int y)
 15 {
 16     if (x < y) swap(x, y);
 17     while (y)
 18     {
 19         int tmp = x % y;
 20         x = y;
 21         y = tmp;
 22     }
 23     return x;
 24 }
 25 void addedge(int from, int to, int w)
 26 {
 27     edge[totedge] = EDGE(from, to, w, Head[from]);
 28     Head[from] = totedge++;
 29     edge[totedge] = EDGE(to, from, w, Head[to]);
 30     Head[to] = totedge++;
 31 }
 32 
 33 int root, MX;//树的重心、找到的最大子树的最小结点数目
 34 int nums[maxn];//i为根,子树的结点数量(包括自己)
 35 int mxson[maxn];//i为根,最大的子树
 36 bool vis[maxn];
 37 int n, ans, k;//输入的树的大小、结果、指定的路径长度的最大值
 38 int sn;//当前根下,总树的大小
 39 void init()
 40 {
 41     memset(Head, -1, sizeof(Head));
 42     totedge = 0;
 43     memset(vis, 0, sizeof(vis));
 44     MX = INF;
 45     ans = 0;
 46     sn = n;
 47 }
 48 void getRoot(int u, int fa)
 49 {//找到树的重心
 50     nums[u] = 1, mxson[u] = 0;
 51     for (int i = Head[u]; i != -1; i = edge[i].next)
 52     {
 53         int v = edge[i].to;
 54         if (vis[v] || v == fa)continue;
 55         getRoot(v, u);
 56         nums[u] += nums[v];
 57         mxson[u] = max(mxson[u], nums[v]);
 58     }
 59     mxson[u] = max(mxson[u], sn - nums[u]);// n - nums[u]是经过父亲节点的子树大小
 60     if (mxson[u] < MX) root = u, MX = mxson[u];
 61 
 62 }
 63 int dis[3];
 64 void getdis(int u, int fa, int dist)
 65 {//找到子树所有结点到当前根的距离
 66     dis[dist]++;
 67     for (int i = Head[u]; i != -1; i = edge[i].next)
 68     {
 69         int v = edge[i].to;
 70         if (vis[v] || v == fa) continue;
 71         getdis(v, u, (dist + edge[i].w)%3);
 72     }
 73 }
 74 int solve(int r, int len)
 75 {
 76     memset(dis, 0, sizeof(dis));
 77     getdis(r, 0, len%3);
 78     return dis[0] * (dis[0]-1)+dis[0] + dis[1] * dis[2] * 2;
 79 }
 80 void pointDivide(int tr)
 81 {
 82     ans += solve(tr, 0);//求解经过tr的所有路径(点对)
 83     vis[tr] = true;//当前点标记
 84     for (int i = Head[tr]; i != -1; i = edge[i].next)
 85     {//分治处理每一棵子树
 86         int v = edge[i].to;
 87         if (vis[v]) continue;
 88         ans -= solve(v, edge[i].w);//容斥去除重复部分
 89         sn = nums[v];//重设当前总树大小,寻找新的分治点(重心)
 90         root = 0;
 91         MX = INF;
 92         getRoot(v, 0);
 93         pointDivide(root); //递归处理新的分治点
 94     }
 95 }
 96 
 97 int main()
 98 {
 99     while (~scanf("%d", &n))
100     {
101         init();
102         for (int i = 1; i <= n - 1; i++)
103         {
104             int u, v, w;
105             scanf("%d%d%d", &u, &v, &w);
106             addedge(u, v, w);
107         }
108         getRoot(1, 0);
109         pointDivide(root);
110         int gg = gcd(n*n, ans);
111 
112         printf("%d/%d\n", ans/gg,n*n/gg);
113     }
114     return 0;
115 }
View Code

 3、Happy King HDU - 5314

  题意:树上每个结点有个权值。求树上有多少点对两两之间的路径上的最大点权与最小点权之差不超过D。

  思路:点分治。每次计算子树各个结点到根路径上的最大点权与最小点权,当之差不超过D时记录进数组DT。按照最小点权排序。对于i,在(DT,DT+i)中找到第一个最小点权大于等于max_i-D的位置j,则i与j~i-1两两组合形成的点对符合要求。由于按照min_i从小到大排序,那么min_j>=max_i-D->max_i-min_j<=D,j+1~i-1之间z满足min_j<=min_z<=min_i,自然max_i-min_z<=D。对于j~i之间的z的max_z,由于max_z-min_z<=D,设m在z+1~i之间,则min_m>=min_z,故max_z-min_m<=D。因此,此间的i-j个点对都符合要求。由于(u,v)与(v,u)为不同点对,故最后答案*2即可。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 using namespace std;
  6 const int maxn = 100000 + 10;
  7 const int INF = 0x3f3f3f3f;
  8 struct EDGE
  9 {
 10     int from, to, next;
 11     EDGE(int ff = 0, int tt = 0, int nn = 0) :from(ff), to(tt),next(nn) {}
 12 }edge[maxn << 1];
 13 int Head[maxn], totedge;
 14 int gcd(int x, int y)
 15 {
 16     if (x < y) swap(x, y);
 17     while (y)
 18     {
 19         int tmp = x % y;
 20         x = y;
 21         y = tmp;
 22     }
 23     return x;
 24 }
 25 void addedge(int from, int to)
 26 {
 27     edge[totedge] = EDGE(from, to, Head[from]);
 28     Head[from] = totedge++;
 29     edge[totedge] = EDGE(to, from, Head[to]);
 30     Head[to] = totedge++;
 31 }
 32 
 33 int root, MX;//树的重心、找到的最大子树的最小结点数目
 34 int val[maxn];
 35 int nums[maxn];//i为根,子树的结点数量(包括自己)
 36 int mxson[maxn];//i为根,最大的子树
 37 bool vis[maxn];
 38 int n,D;
 39 long long ans;
 40 int sn;//当前根下,总树的大小
 41 void init()
 42 {
 43     memset(Head, -1, sizeof(Head));
 44     totedge = 0;
 45     memset(vis, 0, sizeof(vis));
 46     MX = INF;
 47     ans = 0;
 48     sn = n;
 49 }
 50 void getRoot(int u, int fa)
 51 {//找到树的重心
 52     nums[u] = 1, mxson[u] = 0;
 53     for (int i = Head[u]; i != -1; i = edge[i].next)
 54     {
 55         int v = edge[i].to;
 56         if (vis[v] || v == fa)continue;
 57         getRoot(v, u);
 58         nums[u] += nums[v];
 59         mxson[u] = max(mxson[u], nums[v]);
 60     }
 61     mxson[u] = max(mxson[u], sn - nums[u]);// n - nums[u]是经过父亲节点的子树大小
 62     if (mxson[u] < MX) root = u, MX = mxson[u];
 63 
 64 }
 65 
 66 struct info
 67 {
 68     int maxv, minv;
 69     info(int mx = 0, int mn = 0) :maxv(mx), minv(mn){}
 70     friend bool operator <(const info&i1, const info&i2)
 71     {
 72         if (i1.minv == i2.minv) return i1.maxv < i2.maxv;
 73         else return i1.minv < i2.minv;
 74     }
 75 }DT[maxn];
 76 int cnt;
 77 void get_mx_mn(int u, int fa, int mx,int mn)
 78 {//找到子树所有结点到当前根的路径上结点值的最大值和最小值
 79     mx = max(mx, val[u]), mn = min(mn, val[u]);
 80     if (mx - mn <= D) DT[cnt++] = info(mx, mn);
 81     for (int i = Head[u]; i != -1; i = edge[i].next)
 82     {
 83         int v = edge[i].to;
 84         if (vis[v] || v == fa) continue;
 85         get_mx_mn(v, u,mx,mn);
 86     }
 87 }
 88 long long solve(int r, int len)
 89 {
 90     cnt = 0;
 91     get_mx_mn(r, 0,len,len);
 92     sort(DT, DT + cnt);
 93     long long tans = 0;
 94     for (int i = 0; i < cnt; i++)
 95     {
 96         int tmin = DT[i].maxv - D;
 97         int pos = lower_bound(DT, DT + i, info(0, tmin)) - DT;
 98         tans += i - pos;
 99     }
100     return tans;
101 }
102 void pointDivide(int tr)
103 {
104     ans += solve(tr, val[tr]);//求解经过tr的所有路径(点对)
105     vis[tr] = true;//当前点标记
106     for (int i = Head[tr]; i != -1; i = edge[i].next)
107     {//分治处理每一棵子树
108         int v = edge[i].to;
109         if (vis[v]) continue;
110         ans -= solve(v,val[tr]);//容斥去除重复部分
111         sn = nums[v];//重设当前总树大小,寻找新的分治点(重心)
112         root = 0;
113         MX = INF;
114         getRoot(v, 0);
115         pointDivide(root); //递归处理新的分治点
116     }
117 }
118 
119 int main()
120 {
121     int t;
122     scanf("%d", &t);
123     while (t--)
124     {
125         scanf("%d%d", &n, &D);
126         init();
127         for (int i = 1; i <= n; i++) scanf("%d", &val[i]);
128         int mxv = -INF,mnv=INF;
129         for (int i = 1; i <= n - 1; i++)
130         {
131             int u, v;
132             scanf("%d%d", &u, &v);
133             addedge(u, v);
134         }
135         getRoot(1, 0);
136         pointDivide(root);
137         printf("%lld\n", ans*2);
138     }
139     return 0;
140 }
View Code

 4、Garden of Eden HDU - 5977

  题意(等效):有一棵树,每个结点有一种颜色,问有多少点对使得之间的路径覆盖所有种类的颜色。颜色种类为1~10.

  思路:点分治,用二进制记录子树的结点到根的路径的颜色状态,因为如果一条路径状态为11,那么其也符合01、10、00的状态。进行状态转移后,得到每种状态的路径数,然后枚举子树各个结点的状态cur,累计Count[allstates^cur]。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 using namespace std;
  6 const int maxn = 50000 + 10;
  7 const int INF = 0x3f3f3f3f;
  8 const int totstate = 1 << 12;
  9 int type[15];
 10 struct EDGE
 11 {
 12     int from, to, next;
 13     EDGE(int ff = 0, int tt = 0, int nn = 0) :from(ff), to(tt), next(nn) {}
 14 }edge[maxn << 1];
 15 int Head[maxn], totedge;
 16 
 17 void addedge(int from, int to)
 18 {
 19     edge[totedge] = EDGE(from, to, Head[from]);
 20     Head[from] = totedge++;
 21     edge[totedge] = EDGE(to, from, Head[to]);
 22     Head[to] = totedge++;
 23 }
 24 
 25 int root, MX;//树的重心、找到的最大子树的最小结点数目
 26 int val[maxn];
 27 int nums[maxn];//i为根,子树的结点数量(包括自己)
 28 int mxson[maxn];//i为根,最大的子树
 29 bool vis[maxn];
 30 int n, k;
 31 long long ans;
 32 int sn;//当前根下,总树的大小
 33 void init()
 34 {
 35     memset(Head, -1, sizeof(Head));
 36     totedge = 0;
 37     memset(vis, 0, sizeof(vis));
 38     MX = INF;
 39     ans = 0;
 40     sn = n;
 41 }
 42 void getRoot(int u, int fa)
 43 {//找到树的重心
 44     nums[u] = 1, mxson[u] = 0;
 45     for (int i = Head[u]; i != -1; i = edge[i].next)
 46     {
 47         int v = edge[i].to;
 48         if (vis[v] || v == fa)continue;
 49         getRoot(v, u);
 50         nums[u] += nums[v];
 51         mxson[u] = max(mxson[u], nums[v]);
 52     }
 53     mxson[u] = max(mxson[u], sn - nums[u]);// n - nums[u]是经过父亲节点的子树大小
 54     if (mxson[u] < MX) root = u, MX = mxson[u];
 55 
 56 }
 57 int st[maxn],Count[totstate];
 58 int cnt;
 59 void get_type_state(int u, int fa, int tp)
 60 {//找到子树所有结点到当前根的路径上结点值的并的总状态
 61     tp = tp | val[u];
 62     st[cnt++] = tp;
 63     Count[tp]++;
 64     for (int i = Head[u]; i != -1; i = edge[i].next)
 65     {
 66         int v = edge[i].to;
 67         if (vis[v] || v == fa) continue;
 68         get_type_state(v, u,tp);
 69     }
 70 }
 71 long long solve(int r, int tp)
 72 {
 73     cnt = 0;
 74     memset(Count, 0, sizeof(Count));
 75     get_type_state(r, 0, tp);
 76     int allstates = (1 << k) - 1;
 77     for (int i = 1; i <= k; i++)
 78     {//得到路径各个状态的数目
 79         for (int j = allstates; j >= 0; j--)
 80         {
 81             if (!(type[i] & j)) Count[j] += Count[j | type[i]];//如果某个结点到根有1、2种苹果,则其也满足有第1种苹果的状态以及只有第2种苹果的状态
 82         }
 83     }
 84     long long tans = 0;
 85     for (int i = 0; i < cnt; i++)tans += Count[allstates^st[i]];
 86     return tans;
 87 }
 88 void pointDivide(int tr)
 89 {
 90     ans += solve(tr, val[tr]);//求解经过tr的所有路径(点对)
 91     vis[tr] = true;//当前点标记
 92     for (int i = Head[tr]; i != -1; i = edge[i].next)
 93     {//分治处理每一棵子树
 94         int v = edge[i].to;
 95         if (vis[v]) continue;
 96         ans -= solve(v, val[tr]);//容斥去除重复部分
 97         sn = nums[v];//重设当前总树大小,寻找新的分治点(重心)
 98         root = 0;
 99         MX = INF;
100         getRoot(v, 0);
101         pointDivide(root); //递归处理新的分治点
102     }
103 }
104 
105 int main()
106 {
107     type[1] = 1;
108     for (int i = 2; i <= 10; i++) type[i] = type[i - 1] << 1;
109     while (~scanf("%d%d",&n,&k))
110     {
111         init();
112         for (int i = 1; i <= n; i++)
113         {
114             int v;
115             scanf("%d", &v);
116             val[i] = type[v];
117         }
118         for (int i = 1; i <= n - 1; i++)
119         {
120             int u, v;
121             scanf("%d%d", &u, &v);
122             addedge(u, v);
123         }
124         getRoot(1, 0);
125         pointDivide(root);
126         printf("%lld\n", ans);
127     }
128     return 0;
129 }
View Code

 5、震波 HYSBZ - 3730

  题意:有一棵树,点权。两种操作:询问距离u不超过D的所有结点的权值和;修改结点u的权值为v。

  思路:动态点分治、线段树、在线LCA。

  对于每个重心我们开两颗线段树,维护区间和:
  第一棵,对以重心rt为根的子树的结点v,以距离重心rt的距离为下标(线段树区间下标),点权为值插入线段树。
  第二棵,对以重心rt为根的子树的结点v,以距离重心rt的父亲的距离为下标(线段树区间下标),点权为值插入线段树。

  同时记录每个重心的前重心,用以接下来的查询和更新操作。

  LCA用来查询树上两点距离。

  修改时,可以看成是对u结点加上“rem=v-原值”。然后对含有其的每棵线段树的sum值进行更新。对重心i,对其前重心pre的第一棵线段树在位置dis(i,pre)加上rem,对i的第二棵线段树在位置dis(i,pre)加上rem。

  查询时,对当前重心i的第一棵线段树求得[0,D]区间和。然后不断向上更新(爬树高)。对重心i,加上其前重心pre的第一棵线段树在区间[0,D-dis[i,pre]]的权值和,减去i的第二棵线段树在区间[0,D-dis[i,pre]]的权值和。原理是容斥,因为对每棵子树的重心我们都有累计值,但由于在同一子树的值会累计重复(因为其前重心和当前重心我们都累计了,而累计前重心时,同时也把在当前重心的点也统计进去),故需要减去,这就是为什么每个重心要设立两棵线段树的原因。

  1 #include <cstdio>
  2 #include<cstring>
  3 using namespace std;
  4 const int maxn = 500100;
  5 inline void swap(int &x, int &y) {
  6     static int tmp;
  7     tmp = x;
  8     x = y;
  9     y = tmp;
 10 }
 11 inline int Max(const int &a, const int &b) 
 12 {
 13     return a > b ? a : b;
 14 }
 15 /*快读快写*/
 16 inline char get(void) 
 17 {
 18     static char buf[1000000], *p1 = buf, *p2 = buf;
 19     if (p1 == p2) {
 20         p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
 21         if (p1 == p2) return EOF;
 22     }
 23     return *p1++;
 24 }
 25 inline void read(int &x) 
 26 {
 27     x = 0; static char c; bool minus = false;
 28     for (; !(c >= '0' && c <= '9'); c = get()) if (c == '-') minus = true;
 29     for (; c >= '0' && c <= '9'; x = x * 10 + c - '0', c = get()); if (minus) x = -x;
 30 }
 31 inline void write(int x) 
 32 {
 33     if (!x) return (void)puts("0");
 34     if (x < 0) putchar('-'), x = -x;
 35     static short s[12], t;
 36     while (x) s[++t] = x % 10, x /= 10;
 37     while (t) putchar('0' + s[t--]);
 38     putchar('\n');
 39 }
 40 int n, m, ans;
 41 int Head[maxn], totedge;
 42 struct Edge 
 43 {
 44     int to, next;
 45     Edge(void) {}
 46     Edge(const int &to, const int &nxt) : to(to), next(nxt) {}
 47 } edge[maxn << 1];
 48 
 49 inline void add(int from, int to) 
 50 {
 51     edge[totedge] = Edge(to, Head[from]), Head[from] = totedge++;
 52     edge[totedge] = Edge(from, Head[to]), Head[to] = totedge++;
 53 }
 54 /*LCA*/
 55 int dir[maxn], dp[maxn][30];
 56 inline void lca_dfs(int u, int fa) 
 57 {
 58     dp[u][0] = fa;
 59     for (int i = 1; i <= 20; i++) 
 60     {
 61         dp[u][i] = dp[dp[u][i - 1]][i - 1];
 62     }
 63     for (int i = Head[u], v; i!=-1; i = edge[i].next) 
 64     {
 65         v = edge[i].to;
 66         if (v == fa) continue;
 67         dir[v] = dir[u] + 1;
 68         lca_dfs(v, u);
 69     }
 70 }
 71 inline int LCA(int x, int y) 
 72 {
 73     if (dir[x] < dir[y]) swap(x, y);
 74     int tmp = dir[x] - dir[y];
 75     for (int k = 0, j = 1; j <= tmp; j <<= 1, k++)
 76         if (tmp & j) x = dp[x][k];
 77     while (x != y) 
 78     {
 79         int j = 0;
 80         while (dp[x][j] != dp[y][j]) j++;
 81         if (j) j--;
 82         x = dp[x][j], y = dp[y][j];
 83     }
 84     return x;
 85 }
 86 inline int dist(int x, int y) 
 87 {
 88     return dir[x] + dir[y] - (dir[LCA(x, y)] << 1);
 89 }
 90 /*LCA结束*/
 91 int sum[maxn << 4], lson[maxn << 4], rson[maxn << 4];
 92 int sz, id_root[maxn][2];//分配线段树结点、记录所有点分树的根
 93 /*
 94 对于每个重心我们开两颗线段树:
 95 第一棵,对以重心rt为根的子树的结点v,以距离重心rt的距离为下标(线段树区间下标),点权为值插入线段树。调用函
 96 第二棵,对以重心rt为根的子树的结点v,以距离重心rt的父亲的距离为下标(线段树区间下标),点权为值插入线段树。
 97 */
 98 inline void insert(int &rt, int l, int r, int x, int v) 
 99 {//在x的位置加上v
100     if (!rt) rt = ++sz; sum[rt] += v;
101     if (l == r) return;
102     int mid = (l + r) >> 1;
103     if (mid >= x) insert(lson[rt], l, mid, x, v);
104     else insert(rson[rt], mid + 1, r, x, v);
105 }
106 inline int query(int rt, int l, int r, int L, int R) 
107 {//询问[L,R]之间的权值和
108     if (!rt) return 0;
109     if (l >= L && r <= R) return sum[rt];
110     int mid = (l + r) >> 1;
111     if (R <= mid) return query(lson[rt], l, mid, L, R);
112     else if (L > mid) return query(rson[rt], mid + 1, r, L, R);
113     else return query(lson[rt], l, mid, L, mid) + query(rson[rt], mid + 1, r, mid + 1, R);
114 }
115 bool vis[maxn];
116 int d[maxn];//距离
117 int val[maxn];//结点值
118 int sn;//当前树的总节点数
119 int nums[maxn];//子树的节点数
120 int root, mxson[maxn], pre[maxn];//树的重心、含有最多结点的子树的数目、前重心
121 
122 inline void maketree(int rt, int l, int u, int fa) 
123 {//构建线段树
124     insert(id_root[rt][l], 0, n, d[u], val[u]);
125     for (int i = Head[u], v; i!=-1; i = edge[i].next) 
126     {
127         v = edge[i].to;
128         if (v == fa || vis[v]) continue;
129         d[v] = d[u] + 1;
130         maketree(rt, l, v, u);
131     }
132 }
133 inline void getroot(int u, int fa) 
134 {//获取树的重心
135     nums[u] = 1; 
136     mxson[u] = 0;
137     for (int i = Head[u], v; i!=-1; i = edge[i].next) 
138     {
139         v = edge[i].to;
140         if (v == fa || vis[v]) continue;
141         getroot(v, u);
142         nums[u] += nums[v];
143         mxson[u] = Max(mxson[u], nums[v]);
144     }
145     mxson[u] = Max(mxson[u], sn - nums[u]);
146     if (mxson[u] < mxson[root]) root = u;
147 }
148 inline void pointDivide(int u) 
149 {//构建点分树
150     vis[u] = 1; d[u] = 0; 
151     maketree(u, 0, u, 0);
152     for (int i = Head[u], v; i!=-1; i = edge[i].next) 
153     {
154         v = edge[i].to;
155         if (vis[v]) continue;
156         sn = nums[v]; root = 0;
157         d[v] = 1;
158         getroot(v, 0); 
159         maketree(root, 1, v, u);
160         pre[root] = u;
161         pointDivide(root);
162     }
163 }
164 inline int ask(int u, int k) 
165 {
166     int ret = query(id_root[u][0], 0, n, 0, k);
167     for (int i = u; pre[i]; i = pre[i]) 
168     {
169         int rem = dist(u, pre[i]);
170         ret += query(id_root[pre[i]][0], 0, n, 0, k - rem);
171         ret -= query(id_root[i][1], 0, n, 0, k - rem);
172     }
173     return ret;
174 }
175 inline void update(int u, int k) 
176 {
177     int sub = k - query(id_root[u][0], 0, n, 0, 0);
178     insert(id_root[u][0], 0, n, 0, sub);
179     for (int i = u; pre[i]; i = pre[i]) 
180     {
181         int cn = dist(u, pre[i]);
182         insert(id_root[pre[i]][0], 0, n, cn, sub);
183         insert(id_root[i][1], 0, n, cn, sub);
184     }
185 }
186 int main(void) 
187 {
188     memset(Head, -1, sizeof(Head));
189     read(n), read(m);
190     for (int i = 1; i <= n; i++) read(val[i]);
191     for (int i = 1, u, v; i < n; i++) {
192         read(u), read(v);
193         add(u, v);
194     }
195     sn = mxson[0] = n;
196     lca_dfs(1, 0);
197     getroot(1, 0);
198     pointDivide(root);
199 
200     for (int i = 1, op, x, y; i <= m; i++) {
201         read(op), read(x), read(y);
202         x ^= ans, y ^= ans;
203         if (op) update(x, y);
204         else write(ans = ask(x, y));
205     }
206     return 0;
207 }
View Code

 6、Boatherds POJ - 2114

  题意:求树上是否存在点对,其两点之间路径和为恰好为K。

  思路:点分治。计算子树所有结点到当前根的距离,排序后二分查找计算点对的数目。(注:在solve函数中调用getdis前如果将dis数组memset一下就会超时,去掉后4、5百ms就能过,很迷。)

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 using namespace std;
  6 const int maxn = 10000 + 10;
  7 const int INF = 0x3f3f3f3f;
  8 struct EDGE
  9 {
 10     int from, to, w, next;
 11     EDGE(int ff = 0, int tt = 0, int ww = 0, int nn = 0) :from(ff), to(tt), w(ww), next(nn) {}
 12 }edge[maxn << 1];
 13 int Head[maxn], totedge;
 14 void addedge(int from, int to, int w)
 15 {
 16     edge[totedge] = EDGE(from, to, w, Head[from]);
 17     Head[from] = totedge++;
 18     edge[totedge] = EDGE(to, from, w, Head[to]);
 19     Head[to] = totedge++;
 20 }
 21 
 22 int root, MX;//树的重心、找到的最大子树的最小结点数目
 23 int nums[maxn];//i为根,子树的结点数量(包括自己)
 24 int mxson[maxn];//i为根,最大的子树
 25 bool vis[maxn];
 26 int n, ans, K;//输入的树的大小、结果、指定的路径长度
 27 int sn;//当前根下,总树的大小
 28 
 29 
 30 void getRoot(int u, int fa)
 31 {//找到树的重心
 32     nums[u] = 1, mxson[u] = 0;
 33     for (int i = Head[u]; i != -1; i = edge[i].next)
 34     {
 35         int v = edge[i].to;
 36         if (vis[v] || v == fa)continue;
 37         getRoot(v, u);
 38         nums[u] += nums[v];
 39         mxson[u] = max(mxson[u], nums[v]);
 40     }
 41     mxson[u] = max(mxson[u], sn - nums[u]);// n - nums[u]是经过父亲节点的子树大小
 42     if (mxson[u] < MX) root = u, MX = mxson[u];
 43 
 44 }
 45 int dis[maxn],cnt;//记录当前得到的距离数目
 46 void getdis(int u, int fa, int dist)
 47 {//找到子树所有结点到当前根的距离
 48     if(dist<=K)dis[++cnt] = dist;
 49     for (int i = Head[u]; i != -1; i = edge[i].next)
 50     {
 51         int v = edge[i].to;
 52         if (vis[v] || v == fa) continue;
 53         getdis(v, u, dist + edge[i].w);
 54     }
 55 }
 56 int solve(int r, int len)
 57 {
 58     cnt = 0;
 59     getdis(r, 0, len);
 60     sort(dis + 1, dis + 1 + cnt);
 61     int L = 1, R = cnt;
 62     int tans = 0;
 63     while (L< R)
 64     {
 65         if (dis[R] + dis[L] == K)
 66         {
 67             if (dis[L] == dis[R])
 68             {
 69                 tans += (R - L + 1)*(R - L) / 2;
 70                 break;
 71             }
 72             int ll = L, rr = R;
 73             while (dis[ll] == dis[L]) ll++;
 74             while (dis[rr] == dis[R]) rr--;
 75             tans += (ll - L)*(R - rr);
 76             L=ll,R=rr;
 77         }
 78         else if (dis[R] + dis[L] < K) L++;
 79         else R--;
 80     }
 81     return tans;
 82 
 83 }
 84 void pointDivide(int tr)
 85 {
 86     ans += solve(tr, 0);//求解经过tr的所有路径(点对)
 87     vis[tr] = true;//当前点标记
 88     for (int i = Head[tr]; i != -1; i = edge[i].next)
 89     {//分治处理每一棵子树
 90         int v = edge[i].to;
 91         if (vis[v]) continue;
 92         ans -= solve(v, edge[i].w);//容斥去除重复部分
 93         sn = nums[v];//重设当前总树大小,寻找新的分治点(重心)
 94         root = 0;
 95         MX = INF;
 96         getRoot(v, 0);
 97         pointDivide(root); //递归处理新的分治点
 98     }
 99 }
100 
101 int main()
102 {
103     while (~scanf("%d", &n)&&n)
104     {
105         memset(Head, -1, sizeof(Head));
106         totedge = 0;
107         for (int i = 1; i <= n; i++)
108         {
109             int v, w;
110             while (~scanf("%d", &v) && v)
111             {
112                 scanf("%d", &w);
113                 addedge(i, v, w);
114             }
115         }
116         while (~scanf("%d", &K) && K)
117         {
118             memset(vis, 0, sizeof(vis));
119             MX = INF;
120             ans = 0;
121             sn = n;
122             getRoot(1, 0);
123             pointDivide(root);
124             if (ans) printf("AYE\n");
125             else printf("NAY\n");
126         }
127         printf(".\n");
128     }
129     return 0;
130 }
View Code

 7、Distance Statistics POJ - 1987

  题意:求树上距离小于等于k的点对数目。

  思路:点分治,同poj 1741。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 using namespace std;
  5 const int maxn = 40000 + 10;
  6 const int INF = 0x3f3f3f3f;
  7 struct EDGE
  8 {
  9     int from, to, w, next;
 10     EDGE(int ff = 0, int tt = 0, int ww = 0, int nn = 0) :from(ff), to(tt), w(ww), next(nn) {}
 11 }edge[maxn << 1];
 12 int Head[maxn], totedge;
 13 void addedge(int from, int to, int w)
 14 {
 15     edge[totedge] = EDGE(from, to, w, Head[from]);
 16     Head[from] = totedge++;
 17     edge[totedge] = EDGE(to, from, w, Head[to]);
 18     Head[to] = totedge++;
 19 }
 20 
 21 int root, MX;//树的重心、找到的最大子树的最小结点数目
 22 int nums[maxn];//i为根,子树的结点数量(包括自己)
 23 int mxson[maxn];//i为根,最大的子树
 24 bool vis[maxn];
 25 int n, ans, k,m;//输入的树的大小、结果、指定的路径长度的最大值
 26 int sn;//当前根下,总树的大小
 27 int cnt;//记录当前得到的距离数目
 28 void init()
 29 {
 30     memset(Head, -1, sizeof(Head));
 31     totedge = 0;
 32     memset(vis, 0, sizeof(vis));
 33     MX = INF;
 34     ans = 0;
 35     sn = n;
 36 }
 37 void getRoot(int u, int fa)
 38 {//找到树的重心
 39     nums[u] = 1, mxson[u] = 0;
 40     for (int i = Head[u]; i != -1; i = edge[i].next)
 41     {
 42         int v = edge[i].to;
 43         if (vis[v] || v == fa)continue;
 44         getRoot(v, u);
 45         nums[u] += nums[v];
 46         mxson[u] = max(mxson[u], nums[v]);
 47     }
 48     mxson[u] = max(mxson[u], sn - nums[u]);// n - nums[u]是经过父亲节点的子树大小
 49     if (mxson[u] < MX) root = u, MX = mxson[u];
 50 
 51 }
 52 int dis[maxn];
 53 void getdis(int u, int fa, int dist)
 54 {//找到子树所有结点到当前根的距离
 55     dis[++cnt] = dist;
 56     for (int i = Head[u]; i != -1; i = edge[i].next)
 57     {
 58         int v = edge[i].to;
 59         if (vis[v] || v == fa) continue;
 60         getdis(v, u, dist + edge[i].w);
 61     }
 62 }
 63 int solve(int r, int len)
 64 {
 65     cnt = 0;
 66     //memset(dis, 0, sizeof(dis));
 67     getdis(r, 0, len);
 68     sort(dis + 1, dis + 1 + cnt);
 69     int L = 1, R = cnt;
 70     int tans = 0;
 71     while (L <= R)
 72     {
 73         if (dis[R] + dis[L] <= k)
 74         {
 75             tans += R - L;//一共有R-L对满足条件(L,L+1)、(L,L+2)……(L,L+R)
 76             L++;
 77         }
 78         else R--;
 79     }
 80     return tans;
 81 
 82 }
 83 void pointDivide(int tr)
 84 {
 85     ans += solve(tr, 0);//求解经过tr的所有路径(点对)
 86     vis[tr] = true;//当前点标记
 87     for (int i = Head[tr]; i != -1; i = edge[i].next)
 88     {//分治处理每一棵子树
 89         int v = edge[i].to;
 90         if (vis[v]) continue;
 91         ans -= solve(v, edge[i].w);//容斥去除重复部分
 92         sn = nums[v];//重设当前总树大小,寻找新的分治点(重心)
 93         root = 0;
 94         MX = INF;
 95         getRoot(v, 0);
 96         pointDivide(root); //递归处理新的分治点
 97     }
 98 }
 99 char dir[20];
100 int main()
101 {
102     while (~scanf("%d%d", &n, &m))
103     {
104         init();
105         for (int i = 1; i <= m; i++)
106         {
107             int u, v, w;
108             scanf("%d%d%d%s", &u, &v, &w,dir);
109             addedge(u, v, w);
110         }
111         scanf("%d", &k);
112         getRoot(1, 0);
113         pointDivide(root);
114         printf("%d\n", ans);
115     }
116     return 0;
117 }
View Code

 8、Balancing Act POJ - 1655

  题意:求树上一点,其最大子树的节点数最小。

  思路:求解树的重心。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn = 20000 + 10;
 6 const int INF = 0x3f3f3f3f;
 7 struct EDGE
 8 {
 9     int from, to, next;
10     EDGE(int ff = 0, int tt = 0, int nn = 0) :from(ff), to(tt), next(nn) {}
11 }edge[maxn << 1];
12 int Head[maxn], totedge;
13 void addedge(int from, int to)
14 {
15     edge[totedge] = EDGE(from, to,  Head[from]);
16     Head[from] = totedge++;
17     edge[totedge] = EDGE(to, from, Head[to]);
18     Head[to] = totedge++;
19 }
20 
21 int root, MX;//树的重心、找到的最大子树的最小结点数目
22 int nums[maxn];//i为根,子树的结点数量(包括自己)
23 int mxson[maxn];//i为根,最大的子树
24 bool vis[maxn];
25 int n, ans;//输入的树的大小、结果
26 int sn;//当前根下,总树的大小
27 int cnt;//记录当前得到的距离数目
28 void init()
29 {
30     memset(Head, -1, sizeof(Head));
31     totedge = 0;
32     memset(vis, 0, sizeof(vis));
33     MX = INF;
34     ans = 0;
35     sn = n;
36 }
37 void getRoot(int u, int fa)
38 {//找到树的重心
39     nums[u] = 1, mxson[u] = 0;
40     for (int i = Head[u]; i != -1; i = edge[i].next)
41     {
42         int v = edge[i].to;
43         if (vis[v] || v == fa)continue;
44         getRoot(v, u);
45         nums[u] += nums[v];
46         mxson[u] = max(mxson[u], nums[v]);
47     }
48     mxson[u] = max(mxson[u], sn - nums[u]);// n - nums[u]是经过父亲节点的子树大小
49     if (mxson[u] < MX) root = u, MX = mxson[u];
50 
51 }
52 
53 int main()
54 {
55     int t;
56     scanf("%d", &t);
57 
58     while (t--)
59     {
60         scanf("%d", &n);
61         init();
62         for (int i = 1; i <= n - 1; i++)
63         {
64             int u, v;
65             scanf("%d%d", &u, &v);
66             addedge(u, v);
67         }
68         getRoot(1, 0);
69         printf("%d %d\n", root,MX);
70     }
71     return 0;
72 }
View Code

 9、最短路径树问题 HYSBZ - 4016//////Shortest-path tree hdu-4871

  题意:给出一个无向图,求最短路径树(有多个最短路时,选择路径结点的字典序较小的),在树上求路径含有结点数为k的最长路径以及对应的路径数目。

  思路:最短路+点分治。

  1、求最短路径树:先对边排序,因为是最后用链式前向星建树,所以排序时对入点从大到小排,这样加入链式前向星是从小到大的,spfa后dfs建树。

  2、点分治。用dis[maxn], Count[maxn]维护路径上结点数为i时(不计根)的最大路径长度以及条数。对于当前重心u,处理其每棵子树,先更新答案值,再更新dis、Count数组。

  3、hdu是多组输入,注意调整下代码即可。

  4、每次对树的重心操作时,需要重新对dis、Count数组初始化,其上限为当前树的最大深度。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<queue>
  4 #include<algorithm>
  5 #include<cstring>
  6 using namespace std;
  7 const int maxn = 30000 + 10;
  8 const int maxm = 60000 + 10;
  9 const int INF = 0x3f3f3f3f;
 10 struct EDGE
 11 {
 12     int from, to, w, next;
 13     EDGE(int ff = 0, int tt = 0, int ww = 0, int nn = 0) :from(ff), to(tt), w(ww), next(nn) {}
 14     friend bool operator<(const EDGE&e1, const EDGE&e2)
 15     {
 16         if (e1.from == e2.from) return e1.to > e2.to;
 17         else return e1.from < e2.from;
 18     }
 19 }edge1[maxn << 1], edge2[maxm << 1], edge3[maxm << 1];
 20 int Head1[maxn], Head2[maxn], totedge1, totedge2;
 21 void addedge(EDGE *edge, int *Head, int &totedge, int from, int to, int w)
 22 {
 23     edge[totedge] = EDGE(from, to, w, Head[from]);
 24     Head[from] = totedge++;
 25     edge[totedge] = EDGE(to, from, w, Head[to]);
 26     Head[to] = totedge++;
 27 }
 28 
 29 int root, MX;//树的重心、找到的最大子树的最小结点数目
 30 int nums[maxn];//i为根,子树的结点数量(包括自己)
 31 int mxson[maxn];//i为根,最大的子树
 32 bool vis[maxn];
 33 int n;//输入的树的大小、结果
 34 int m, k;
 35 int sn;//当前根下,总树的大小
 36 int ans1, ans2;
 37 int mxdep;
 38 void init()
 39 {
 40     memset(Head1, -1, sizeof(Head1));
 41     memset(Head2, -1, sizeof(Head2));
 42     totedge1 = totedge2 = 0;
 43     memset(vis, 0, sizeof(vis));
 44     MX = INF;
 45     ans1 = 0, ans2 = 0;
 46     sn = n;
 47 }
 48 void getRoot(EDGE *edge, int *Head, int u, int fa)
 49 {//找到树的重心
 50     nums[u] = 1, mxson[u] = 0;
 51     for (int i = Head[u]; i != -1; i = edge[i].next)
 52     {
 53         int v = edge[i].to;
 54         if (vis[v] || v == fa)continue;
 55         getRoot(edge, Head, v, u);
 56         nums[u] += nums[v];
 57         mxson[u] = max(mxson[u], nums[v]);
 58     }
 59     mxson[u] = max(mxson[u], sn - nums[u]);// n - nums[u]是经过父亲节点的子树大小
 60     if (mxson[u] < MX) root = u, MX = mxson[u];
 61 
 62 }
 63 int dis[maxn], Count[maxn];//路径上结点数为i时(不计根)的最大路径长度以及条数
 64 void getdis(EDGE *edge, int *Head, int u, int fa, int dist, int dep)
 65 {
 66     if (dep >= k) return;
 67     if (dist > dis[dep]) dis[dep] = dist, Count[dep] = 1;
 68     else if (dist == dis[dep]) Count[dep]++;
 69     for (int i = Head[u]; i != -1; i = edge[i].next)
 70     {
 71         int v = edge[i].to;
 72         if (vis[v] || v == fa) continue;
 73         getdis(edge, Head, v, u, dist + edge[i].w, dep + 1);
 74     }
 75 }
 76 void updatedis(EDGE *edge, int *Head, int u, int fa, int dist, int dep)
 77 {
 78     if (dep >= k) return;
 79     if (Count[k - dep - 1] && dist + dis[k - dep - 1] > ans1)
 80     {
 81         ans1 = dist + dis[k - dep - 1], ans2 = Count[k - dep - 1];
 82     }
 83     else if (dist + dis[k - dep - 1] == ans1) ans2 += Count[k - dep - 1];
 84     for (int i = Head[u]; i != -1; i = edge[i].next)
 85     {
 86         int v = edge[i].to;
 87         if (vis[v] || v == fa) continue;
 88         updatedis(edge, Head, v, u, dist + edge[i].w, dep + 1);
 89     }
 90 }
 91 void get_mxdep(EDGE *edge, int *Head, int u, int fa, int dep)
 92 {
 93     mxdep = max(mxdep, dep);
 94     for (int i = Head[u]; i != -1; i = edge[i].next)
 95     {
 96         int v = edge[i].to;
 97         if (vis[v] || v == fa) continue;
 98         get_mxdep(edge, Head, v, u, dep + 1);
 99     }
100 }
101 void solve(EDGE *edge, int *Head, int r)
102 {
103     mxdep = 0;
104     get_mxdep(edge, Head, r, 0, 0);
105     for (int i = 1; i <=k; i++) dis[i] = Count[i] = 0;
106 
107     dis[0] = 0, Count[0] = 1;
108     for (int i = Head[r]; i != -1; i = edge[i].next)
109     {
110         int v = edge[i].to;
111         if (vis[v]) continue;
112         updatedis(edge, Head, v, r, edge[i].w, 1);
113         getdis(edge, Head, v, r, edge[i].w, 1);
114     }
115 }
116 void pointDivide(EDGE *edge, int *Head, int tr)
117 {
118     vis[tr] = true;//当前点标记
119     solve(edge, Head, tr);
120     for (int i = Head[tr]; i != -1; i = edge[i].next)
121     {
122         int v = edge[i].to;
123         if (vis[v]) continue;
124         sn = nums[v];//重设当前总树大小,寻找新的分治点(重心)
125         root = 0;
126         MX = INF;
127         getRoot(edge, Head, v, 0);
128         pointDivide(edge, Head, root); //递归处理新的分治点
129     }
130 }
131 int dis_spfa[maxn];
132 bool spfa(int rt)
133 {
134     for (int i = 1; i <= n; i++) dis_spfa[i] = INF;
135     dis_spfa[rt] = 0, vis[rt] = true;
136     queue<int>q;
137     q.push(rt);
138     while (!q.empty())
139     {
140         int u = q.front();
141         q.pop();
142         vis[u] = false;
143         for (int i = Head2[u]; i != -1; i = edge2[i].next)
144         {
145             int v = edge2[i].to, w = edge2[i].w;
146             if (dis_spfa[v] > dis_spfa[u] + w)
147             {
148                 dis_spfa[v] = dis_spfa[u] + w;
149                 if (!vis[v])
150                 {
151                     vis[v] = true;
152                     q.push(v);
153                 }
154             }
155         }
156     }
157 }
158 void maketree(int rt, int fa)
159 {
160     vis[rt] = true;
161     for (int i = Head2[rt]; i != -1; i = edge2[i].next)
162     {
163         int to = edge2[i].to, w = edge2[i].w;
164         if (!vis[to] && dis_spfa[to] == dis_spfa[rt] + w && to != fa)
165         {
166             addedge(edge1, Head1, totedge1, rt, to, w);
167             maketree(to, rt);
168         }
169     }
170 }
171 int main()
172 {
173     int t;
174     scanf("%d", &t);
175     while (t--)
176     {
177         scanf("%d%d%d", &n, &m, &k);
178         init();
179         int cnt = 0;
180         for (int i = 1; i <= m; i++)
181         {
182             int u, v, w;
183             scanf("%d%d%d", &u, &v, &w);
184             edge3[cnt++] = EDGE(u, v, w);
185             edge3[cnt++] = EDGE(v, u, w);
186         }
187         sort(edge3, edge3 + cnt);
188         for (int i = 0; i < cnt; i++)
189         {
190             int from = edge3[i].from, to = edge3[i].to, w = edge3[i].w;
191             edge2[totedge2] = EDGE(from, to, w, Head2[from]);
192             Head2[from] = totedge2++;
193         }
194         spfa(1);
195         memset(vis, 0, sizeof(vis));
196         maketree(1, 0);
197         memset(vis, 0, sizeof(vis));
198         getRoot(edge1, Head1, 1, 0);
199         pointDivide(edge1, Head1, root);
200         printf("%d %d\n", ans1, ans2);
201     }
202     return 0;
203 }
204 /*
205 6 6 3
206 1 2 2
207 1 3 1
208 2 4 7
209 2 6 3
210 3 5 1
211 5 6 3
212 
213 10 1
214 */
View Code

 

posted @ 2018-07-27 16:54  萌萌的美男子  阅读(330)  评论(0编辑  收藏  举报