树上倍增、树分治学习总结

树上倍增:

  核心:

    F[u][i] 表示u节点往上走2i步到达的祖先节点,F[u][0]=fa[u],F[u][i]=F[F[u][i-1]][i-1],其他需要维护的数值也与此格式类似

 

【SCOI2016】幸运数字(题目):

  倍增时合并一下线性基即可

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 using namespace std;
  6 
  7 typedef long long LL;
  8 
  9 const int N = 20010;
 10 
 11 int n, q;
 12 LL G[N], dep[N], ans[N];
 13 LL fa[N][21], p[N][21][62];
 14 int h[N], num[N << 1], nex[N << 1], dqx;
 15 
 16 inline void add(int a, int b)
 17 {
 18     num[dqx] = b;
 19     nex[dqx] = h[a];
 20     h[a] = dqx++;
 21 }
 22 
 23 inline void get_p(LL *a, LL val)
 24 {
 25     for (int i = 61; i >= 0; i--)
 26     {
 27         if ((val >> i) & 1)
 28         {
 29             if (!a[i])
 30             {
 31                 a[i] = val;
 32                 break;
 33             }
 34             val ^= a[i];
 35         }
 36     }
 37 }
 38 
 39 void dfs(int u, int f)
 40 {
 41     fa[u][0] = f;
 42     dep[u] = dep[f] + 1;
 43     for (int i = h[u]; ~i; i = nex[i])
 44     {
 45         int j = num[i];
 46         if (j == f) continue;
 47         dfs(j, u);
 48     }
 49 }
 50 
 51 void merge(LL* a, LL* b)
 52 {
 53     for (int i = 61; i >= 0; i--)
 54     {
 55         if (b[i]) get_p(a, b[i]);
 56     }
 57 }
 58 
 59 void get_lca()
 60 {
 61     for (int j = 1; j < 20; j++)
 62     {
 63         for (int i = 1; i <= n; i++)
 64         {
 65             fa[i][j] = fa[fa[i][j - 1]][j - 1];
 66             memcpy(p[i][j], p[i][j - 1], sizeof(p[i][j - 1]));
 67             merge(p[i][j], p[fa[i][j - 1]][j - 1]);
 68         }
 69     }
 70 }
 71 
 72 inline void LCA(int u, int v)
 73 {
 74     if (dep[u] < dep[v]) swap(u, v);
 75 
 76     for (int i = 20; i >= 0; i--)
 77     {
 78         if (dep[fa[u][i]] >= dep[v])
 79         {
 80             merge(ans, p[u][i]);
 81             u = fa[u][i];
 82         }
 83     }
 84         
 85     if (u == v)
 86     {
 87         merge(ans, p[u][0]);
 88         return;
 89     }
 90 
 91     for (int i = 20; i >= 0; i--)
 92     {
 93         if (fa[u][i] != fa[v][i])
 94         {
 95             merge(ans, p[u][i]), merge(ans, p[v][i]);
 96             u = fa[u][i], v = fa[v][i];
 97         }
 98     }
 99         
100     merge(ans, p[u][0]);
101     merge(ans, p[v][0]);
102     merge(ans, p[fa[u][0]][0]);
103 }
104 
105 int main()
106 {
107     scanf("%d%d", &n, &q);
108     for (int i = 1; i <= n; i++)
109     {
110         scanf("%lld", &G[i]);
111         get_p(p[i][0], G[i]);
112     }
113 
114     memset(h, -1, sizeof(h));
115     for (int i = 1; i <= n - 1; i++)
116     {
117         int a, b;
118         scanf("%d%d", &a, &b);
119         add(a, b), add(b, a);
120     }
121 
122     dfs(1, 0);
123     get_lca();
124 
125     for (int i = 1; i <= q; i++)
126     {
127         memset(ans, 0, sizeof(ans));
128 
129         int a, b;
130         scanf("%d%d", &a, &b);
131         LCA(a, b);
132         LL res = 0;
133 
134         for (int j = 61; j >= 0; j--) res = max(res, res ^ (LL)ans[j]);
135         printf("%lld\n", res);
136     }
137 }
View Code

 

【NOI2018】归程(题目):

  进行一次Dijkstra,算出每个点到根(1号点)的最短距离dis,再把每条边按照海拔进行排序后进行kruskal重构树,重构树中的每个节点存储的是它代表的边的海拔以及他的子树中最小的dis(相当于是距离1最近的点的距离)。求值时倍增上去,第一个上不去的点的dis就是答案。

  ps:关于kurskal重构树

  树中除了叶节点是原树的点外其他的节点都代表原树的边,当要连接两个点时,将两个点所属的并查集的最高点合并,这两个最高点的父节点便变成代替当前这条边的节点。

  如图,假如有一条编号为6的边要连接1,3两个点,则将1和5的父节点变成6(图中的1,2,3皆为点,其余为边)。

  

 

 

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 typedef pair<int, int> PII;
  9 
 10 const int N = 2000010;
 11 
 12 int n, m;
 13 int fa[N];
 14 int dis[N];
 15 int dep[N], F[N][20];
 16 int h1[N], num1[N], nex1[N], dqx1;
 17 int h[N], num[N], nex[N], v[N], dqx;
 18 bool st[N];
 19 
 20 struct Edge
 21 {
 22     int a, b, l, v;
 23     Edge() {}
 24     Edge(int a, int b, int l, int v) : a(a), b(b), l(l), v(v) {}
 25 }; 
 26 
 27 Edge edge[N << 2], p[N << 2];
 28 
 29 void add(int a, int b, int c)
 30 {
 31     num[dqx] = b;
 32     v[dqx] = c;
 33     nex[dqx] = h[a];
 34     h[a] = dqx++;
 35 }
 36 
 37 void Dijkstra()
 38 {
 39     memset(st, 0, sizeof(st));
 40     memset(dis, 0x3f, sizeof(dis));
 41 
 42     priority_queue<PII, vector<PII>, greater<PII> > que;
 43     dis[1] = 0;
 44     que.push(make_pair(dis[1], 1));
 45 
 46     while (!que.empty())
 47     {
 48         PII t = que.top();
 49         que.pop();
 50         int u = t.second, d = t.first;
 51 
 52         if (st[u]) continue;
 53         st[u] = true;
 54 
 55         for (int i = h[u]; ~i; i = nex[i])
 56         {
 57             int j = num[i];
 58             if (st[j]) continue;
 59             if (dis[j] > dis[u] + v[i])
 60             {
 61                 dis[j] = dis[u] + v[i];
 62                 que.push(make_pair(dis[j], j));
 63             }
 64         }
 65     }
 66 }
 67 
 68 bool cmp(Edge a, Edge b)
 69 {
 70     return a.v > b.v;
 71 }
 72 
 73 void addx(int a, int b)
 74 {
 75     num1[dqx1] = b;
 76     nex1[dqx1] = h1[a];
 77     h1[a] = dqx1++;
 78 }
 79 
 80 void dfs(int u, int f)
 81 {
 82     dep[u] = dep[f] + 1;
 83     F[u][0] = f;
 84 
 85     for (int i = 1; i <= 19; i++)
 86     {
 87         F[u][i] = F[F[u][i - 1]][i - 1];
 88     }
 89 
 90      for (int i = h1[u]; ~i; i = nex1[i])
 91     {
 92         int j = num1[i];
 93         if (j == f) continue;
 94         dfs(j, u);
 95         p[u].l = min(p[u].l, p[j].l);
 96     }
 97 }
 98 
 99 int find1(int x)
100 {
101     return fa[x] == x ? x : fa[x] = find1(fa[x]);
102 }
103 
104 void kruskal()
105 {
106     int tot = 0, cnt = n;
107 
108     for (int i = 1; i <= (n << 1); i++) fa[i] = i;
109     sort(edge + 1, edge + m + 1, cmp);
110 
111     memset(h1, -1, sizeof(h1));
112     for (int i = 1; i <= m; i++)
113     {
114         int a = edge[i].a, b = edge[i].b;
115         int fx = find1(a), fy = find1(b);
116         if (fx == fy) continue;
117 
118         addx(++cnt, fx), addx(cnt, fy);
119 
120         fa[fx] = cnt, fa[fy] = cnt;
121 
122         p[cnt].v = edge[i].v;
123         tot++;
124         if (tot == n - 1) break;
125     }
126 
127     dfs(cnt, 0);
128 }
129 
130 int query(int a, int v)
131 {
132     for (int i = 19; i >= 0; i--) if (dep[a] - (1 << i) > 0 && p[F[a][i]].v > v) a = F[a][i];
133     return p[a].l;
134 }
135 
136 void init()
137 {
138     dqx = dqx1 = 0;
139     memset(edge, 0, sizeof(edge));
140     memset(h, -1, sizeof(h));
141     memset(h1, -1, sizeof(h1));
142     memset(F, 0, sizeof(F));
143 }
144 
145 int main()
146 {
147     int T;
148     scanf("%d", &T);
149     while (T--)
150     {
151         init();
152 
153         scanf("%d%d", &n, &m);
154 
155         for (int i = 1; i <= m; i++)
156         {
157             int a, b, l, v;
158             scanf("%d%d%d%d", &a, &b, &l, &v);
159             edge[i] = Edge(a, b, l, v);
160             add(a, b, l), add(b, a, l);
161         }
162 
163         for (int i = n + 1; i <= n * 2; i++) p[i].l = 2e9 + 7;
164 
165         Dijkstra();
166         for (int i = 1; i <= n; i++) p[i].l = dis[i];
167 
168         kruskal();
169 
170         int ans = 0;
171         int q, k, s;
172         scanf("%d%d%d", &q, &k, &s);
173         while (q--)
174         {
175             int u0, p0;
176             scanf("%d%d", &u0, &p0);
177             u0 = (u0 + k * ans - 1) % n + 1;
178             p0 = (p0 + k * ans) % (s + 1);
179 
180             ans = query(u0, p0);
181             printf("%d\n", ans);
182         }
183 
184     }
185 }
View Code

 

【美团 CodeM 复赛】城市网络(题目):

  对于每次询问,新建一个节点,与u相连,权值为c。之后进行dfs,每个节点的父节点定为深度最深的权值比他大的点,求答案时从该询问对应新建的节点网上倍增,每次倍增时答案加上倍增往上跳的节点数。

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<vector>
 4 #include<iostream>
 5 #include<algorithm>
 6 using namespace std;
 7 
 8 const int N = 200010;
 9 
10 int n, m;
11 int k, t;
12 int a[N], dep[N], to[N], F[20][N];
13 
14 vector<int> e[N];
15 
16 void dfs(int x, int f)
17 {
18     int pos = f;
19     for (int i = 19; i >= 0; i--)
20     {
21         if (F[i][pos] && a[F[i][pos]] <= a[x])
22         {
23             pos = F[i][pos];
24         }
25     }
26 
27     if (a[pos] > a[x]) F[0][x] = pos;
28     else F[0][x] = F[0][pos];
29 
30     for (int i = 1; F[i - 1][F[i - 1][x]]; i++)
31     {
32         F[i][x] = F[i - 1][F[i - 1][x]];
33     }
34     dep[x] = dep[f] + 1; 
35 
36     for (int i : e[x])
37     {
38         if (i == f)continue;
39         dfs(i, x);
40     }
41 }
42 
43 int main()
44 {
45     int q;
46     scanf("%d%d", &n, &q);
47 
48     for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
49     for (int i = 1; i < n; i++)
50     {
51         int a, b;
52         scanf("%d%d", &a, &b);
53         e[a].push_back(b), e[b].push_back(a);
54     }
55 
56     for (int i = 1; i <= q; i++)
57     {
58         int x, y, z;
59         scanf("%d%d%d", &x, &y, &z);
60         a[n + i] = z;
61         e[n + i].push_back(x);
62         e[x].push_back(n + i);
63         to[n + i] = y;
64     }
65 
66     dfs(1, 0);
67 
68     for (int i = n + 1; i <= n + q; i++)
69     {
70         int ret = 0, pos = i;
71         for (int j = 19; j >= 0; j--)
72         {
73             if (dep[F[j][pos]] >= dep[to[i]])
74             {
75                 ret += (1 << j);
76                 pos = F[j][pos];
77             }
78         }
79         printf("%d\n", ret);
80     }
81     return 0;
82 }
View Code

 

【BJWC2010】严格次小生成树(题目):

  倍增数组多存储一个边的次大值,之后枚举边时若和最大值相同就比较次大值。

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 using namespace std;
  6 
  7 typedef long long LL;
  8 
  9 const int N = 500010;
 10 const LL INF = 2e18 + 7;
 11 
 12 int n, m;
 13 int F[N][20], dep[N], fa[N];
 14 LL ma[N][20], mi[N][20];
 15 int h[N], num[N], nex[N], v[N], dqx;
 16 bool st[N];
 17 
 18 struct Edge
 19 {
 20     int a, b, d;
 21     Edge() {}
 22 };
 23 
 24 Edge e[N];
 25 
 26 bool cmp(Edge a, Edge b)
 27 {
 28     return a.d < b.d;
 29 }
 30 
 31 void add(int a, int b, int c)
 32 {
 33     num[dqx] = b;
 34     v[dqx] = c;
 35     nex[dqx] = h[a];
 36     h[a] = dqx++;
 37 }
 38 
 39 int find1(int x)
 40 {
 41     return fa[x] == x ? x : fa[x] = find1(fa[x]);
 42 }
 43 
 44 void dfs(int u, int pa)
 45 {
 46     F[u][0] = pa;
 47     dep[u] = dep[pa] + 1;
 48 
 49     for (int i = h[u]; ~i; i = nex[i])
 50     {
 51         int j = num[i];
 52         if (j == pa) continue;
 53         ma[j][0] = v[i];
 54         mi[j][0] = -INF;
 55         dfs(j, u);
 56     }
 57 }
 58 
 59 LL query_max(int a, int b, int c)
 60 {
 61     LL ans = -INF;
 62     for (int i = 18; i >= 0; --i)
 63     {
 64         if (dep[F[a][i]] >= dep[b])
 65         {
 66             if (c != ma[a][i]) ans = max(ans, ma[a][i]);
 67             else ans = max(ans, mi[a][i]);
 68             a = F[a][i];
 69         }
 70     }
 71     return ans;
 72 }
 73 
 74 int LCA(int a, int b)
 75 {
 76     if (dep[a] < dep[b]) swap(a, b);
 77 
 78     for (int i = 18; i >= 0; i--)
 79     {
 80         if (dep[F[a][i]] >= dep[b]) a = F[a][i];
 81     }
 82     if (a == b) return a;
 83 
 84     for (int i = 18; i >= 0; i--)
 85     {
 86         if (F[a][i] != F[b][i])
 87         {
 88             a = F[a][i];
 89             b = F[b][i];
 90         }
 91     }
 92 
 93     return F[a][0];
 94 }
 95 
 96 int main()
 97 {
 98     scanf("%d%d", &n, &m);
 99     for (int i = 1; i <= m; i++)
100     {
101         scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].d);
102     }
103 
104     sort(e + 1, e + 1 + m, cmp);
105 
106     LL res = 0;
107     memset(h, -1, sizeof(h));
108     for (int i = 1; i <= n; i++) fa[i] = i;
109     for (int i = 1; i <= m; i++)
110     {
111         int a = e[i].a, b = e[i].b, d = e[i].d;
112         int fx = find1(a), fy = find1(b);
113 
114         if (fx == fy) continue;
115         fa[fx] = fy;
116         add(a, b, d);
117         add(b, a, d);
118         res += d;
119 
120         st[i] = true;
121     }
122 
123     mi[1][0] = -INF;
124     dfs(1, 0);
125     
126     for (int i = 1; i <= 18; ++i)
127     {
128         for (int j = 1; j <= n; ++j)
129         {
130             F[j][i] = F[F[j][i - 1]][i - 1];
131             ma[j][i] = max(ma[j][i - 1], ma[F[j][i - 1]][i - 1]);
132             mi[j][i] = max(mi[j][i - 1], mi[F[j][i - 1]][i - 1]);
133             if (ma[j][i - 1] > ma[F[j][i - 1]][i - 1]) mi[j][i] = max(mi[j][i], ma[F[j][i - 1]][i - 1]);
134             else if (ma[j][i - 1] < ma[F[j][i - 1]][i - 1]) mi[j][i] = max(mi[j][i], ma[j][i - 1]);
135         }
136     }
137         
138     LL ans = INF;
139     for (int i = 1; i <= m; i++)
140     {
141         if (st[i]) continue;
142 
143         int a = e[i].a, b = e[i].b, d = e[i].d;
144 
145         int lca = LCA(a, b);
146         LL m1 = query_max(a, lca, d);
147         LL m2 = query_max(b, lca, d);
148         ans = min(ans, res - max(m1, m2) + d);
149     }
150 
151     printf("%lld", ans);
152 }
View Code

 

【CF519E】A and B and Lecture Rooms(题目):

  如图,若a点为16,b点为15,先求出他们的LCA,为2,这时候a到b的路径的中点为5,答案就是size[5]-size[7]。

  若a点为16,b点为8,LCA为2,这时候中点就是LCA,答案就是n-size[4]-size[5]。

  若两点的深度差为奇数,则无解。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 using namespace std;
  6 
  7 const int N = 200010;
  8 
  9 int n;
 10 int F[N][20];
 11 int size1[N], dep[N];
 12 int h[N], num[N], nex[N], dqx;
 13 
 14 void add(int a, int b) 
 15 {
 16     num[dqx] = b;
 17     nex[dqx] = h[a];
 18     h[a] = dqx++;
 19 }
 20 
 21 void dfs(int u, int pa)
 22 {
 23     size1[u] = 1;
 24     dep[u] = dep[pa] + 1;
 25 
 26     F[u][0] = pa;
 27     for (int i = 1; i <= 18; i++) F[u][i] = F[F[u][i - 1]][i - 1];
 28 
 29     for (int i = h[u]; ~i; i = nex[i])
 30     {
 31         int j = num[i];
 32         if (j == pa) continue;
 33         dfs(j, u);
 34         size1[u] += size1[j];
 35     }
 36 }
 37 
 38 int LCA(int a, int b)
 39 {
 40     if (a == b) return n;
 41     if (dep[a] == dep[b])
 42     {
 43         for (int i = 18; i >= 0; i--)
 44         {
 45             if (F[a][i] != F[b][i])
 46             {
 47                 a = F[a][i];
 48                 b = F[b][i];
 49             }
 50         }
 51         return n - size1[a] - size1[b];
 52     }
 53 
 54     if (dep[a] < dep[b]) swap(a, b);
 55 
 56     if ((dep[a] - dep[b]) % 2) return 0;
 57 
 58     int x = a, d = (dep[a] - dep[b]) / 2;
 59     for (int i = 18; i >= 0; i--)
 60     {
 61         if (dep[F[a][i]] >= dep[b]) a = F[a][i];
 62     }
 63 
 64     if (a == b)
 65     {
 66         d = dep[a] + d;
 67         for (int i = 18; i >= 0; i--)
 68         {
 69             if (dep[F[x][i]] > d) x = F[x][i];
 70         }
 71         return size1[F[x][0]] - size1[x];
 72     }
 73     
 74     for (int i = 18; i >= 0; i--)
 75     {
 76         if (F[a][i] != F[b][i])
 77         {
 78             a = F[a][i], b = F[b][i];
 79         }
 80     }
 81 
 82     d = dep[a] - 1 + d;
 83     for (int i = 18; i >= 0; i--)
 84     {
 85         if (dep[F[x][i]] > d) x = F[x][i];
 86     }
 87 
 88     return size1[F[x][0]] - size1[x];
 89 }
 90 
 91 int main()
 92 {
 93     scanf("%d", &n);
 94 
 95     memset(h, -1, sizeof(h));
 96     for (int i = 1; i < n; i++)
 97     {
 98         int a, b;
 99         scanf("%d%d", &a, &b);
100         add(a, b);
101         add(b, a);
102     }
103 
104     dfs(1, 0);
105 
106     int m;
107     scanf("%d", &m);
108     while (m--)
109     {
110         int a, b;
111         scanf("%d%d", &a, &b);
112         printf("%d\n", LCA(a, b));
113     }
114 }
View Code

 

【ZJOI2012】灾难(题目):

  正反向图都建一个,然后用反向图拓扑排序,这样就能使拓扑排序中每个动物的食物都在它的前面。再遍历拓扑序,用正向图求出当前节点代表的动物的食物的公共祖先,因为只有所有的食物的公共祖先消失后,它的食物才会全部消失,它才会消失。再将它的所有食物的公共祖先向它连边,最后dfs遍历求出每个节点的子树大小,子树大小减一便是灾难值。

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 const int N = 2000010;
  9 
 10 int n;
 11 int ran[N];
 12 int ind[N], dep[N], size1[N], F[N][20];
 13 int h[N], num[N], nex[N], dqx;
 14 int h1[N], num1[N], nex1[N], dqx1;
 15 int h2[N], num2[N], nex2[N], dqx2;
 16 
 17 void add(int a, int b)
 18 {
 19     num[++dqx] = b;
 20     nex[dqx] = h[a];
 21     h[a] = dqx;
 22 }
 23 
 24 void add1(int a, int b)
 25 {
 26     num1[++dqx1] = b;
 27     nex1[dqx1] = h1[a];
 28     h1[a] = dqx1;
 29 }
 30 
 31 void add2(int a, int b)
 32 {
 33     num2[++dqx2] = b;
 34     nex2[dqx2] = h2[a];
 35     h2[a] = dqx2;
 36 }
 37 
 38 void topsort()
 39 {
 40     queue<int> que;
 41     for (int i = 1; i <= n; i++)
 42     {
 43         if (!ind[i]) que.push(i);
 44     }
 45 
 46     int cnt = 0;
 47     while (!que.empty())
 48     {
 49         int u = que.front();
 50         que.pop();
 51         ran[++cnt] = u;
 52 
 53         for (int i = h[u]; i; i = nex[i])
 54         {
 55             int j = num[i];
 56             if (--ind[j] == 0) que.push(j);
 57         }
 58     }
 59 }
 60 
 61 int LCA(int a, int b)
 62 {
 63     if (dep[a] < dep[b]) swap(a, b);
 64 
 65     for (int i = 18; i >= 0; i--)
 66     {
 67         if (dep[F[a][i]] >= dep[b]) a = F[a][i];
 68     }
 69 
 70     if (a == b) return a;
 71 
 72     for (int i = 18; i >= 0; i--)
 73     {
 74         if (F[a][i] != F[b][i])
 75         {
 76             a = F[a][i];
 77             b = F[b][i];
 78         }
 79     }
 80 
 81     return F[a][0];
 82 }
 83 
 84 void dfs(int u)
 85 {
 86     size1[u] = 1;
 87     for (int i = h2[u]; i; i = nex2[i])
 88     {
 89         int j = num2[i];
 90         dfs(j);
 91         size1[u] += size1[j];
 92     }
 93 }
 94 
 95 int main()
 96 {
 97     scanf("%d", &n);
 98 
 99     for (int i = 1; i <= n; i++)
100     {
101         int x, a;
102         while (scanf("%d", &x), x)
103         {
104             ind[i]++;
105             add(x, i);
106             add1(i, x);
107         }
108     }
109 
110     topsort();
111 
112     for (int i = 1; i <= n; i++)
113     {
114         int u = num1[h1[ran[i]]];
115         for (int j = h1[ran[i]]; j; j = nex1[j]) u = LCA(u, num1[j]);
116 
117         add2(u, ran[i]);
118         dep[ran[i]] = dep[u] + 1;
119         F[ran[i]][0] = u;
120         for (int j = 1; j <= 18; j++) F[ran[i]][j] = F[F[ran[i]][j - 1]][j - 1];
121     }
122 
123     dfs(0);
124 
125     for (int i = 1; i <= n; i++) printf("%d\n", size1[i] - 1);
126 }
View Code

 

【洛谷P4197】Peaks(题目):

  kruskal重建树,记录每个节点的子树区间左端点右端点(range),然后用主席树来求第k大数。

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 using namespace std;
  6 
  7 const int N = 6000010;
  8 
  9 int n, m;
 10 int cnt, tot;
 11 int w[N];
 12 int fa[N], F[N][20];
 13 int b[N], lenb;
 14 int range[N][2], dis[N];
 15 int rt[N], ls[N], rs[N], sum[N];
 16 int h[N], num[N], nex[N], dqx;
 17 
 18 struct Edge
 19 {
 20     int a, b, d;
 21     Edge() {}
 22     Edge(int a, int b, int d) :a(a), b(b), d(d) {}
 23 };
 24 
 25 Edge e[N];
 26 
 27 bool cmp(Edge a, Edge b)
 28 {
 29     return a.d < b.d;
 30 }
 31 
 32 void add(int a, int b)
 33 {
 34     num[dqx] = b;
 35     nex[dqx] = h[a];
 36     h[a] = dqx++;
 37 }
 38 
 39 void build(int& q, int l, int r)
 40 {
 41     q = ++cnt;
 42     if (l == r) return;
 43     int mid = (l + r) >> 1;
 44     build(ls[q], l, mid);
 45     build(rs[q], mid + 1, r);
 46 }
 47 
 48 void modify(int pre, int& q, int l, int r, int k)
 49 {
 50     q = ++cnt;
 51     ls[q] = ls[pre], rs[q] = rs[pre], sum[q] = sum[pre] + 1;
 52     if (l == r) return;
 53     int mid = (l + r) >> 1;
 54     if (k <= mid) modify(ls[pre], ls[q], l, mid, k);
 55     else modify(rs[pre], rs[q], mid + 1, r, k);
 56 }
 57 
 58 int query(int pre, int q, int l, int r, int k)
 59 {
 60     if (l == r) return l;
 61     int mid = (l + r) >> 1;
 62     int d = sum[rs[q]] - sum[rs[pre]];
 63     if (k > d) return query(ls[pre], ls[q], l, mid, k-d); // 因为要求第k高,但主席树的值是从小到大的,所以要反着来
 64     else return query(rs[pre], rs[q], mid + 1, r, k);
 65 }
 66 
 67 void dfs(int u)
 68 {
 69     for (int i = 1; i <= 18; i++) F[u][i] = F[F[u][i - 1]][i - 1];
 70 
 71     range[u][0] = tot;
 72     if (!(~h[u]))
 73     {
 74         int k = lower_bound(b + 1, b + lenb + 1, w[u]) - b;
 75         range[u][0] = ++tot;
 76         modify(rt[tot - 1], rt[tot], 1, lenb, k);
 77         return;
 78     }
 79 
 80     for (int i = h[u]; ~i; i = nex[i])
 81     {
 82         dfs(num[i]);
 83     }
 84 
 85     range[u][1] = tot;
 86 }
 87 
 88 int find1(int x)
 89 {
 90     return fa[x] == x ? x : fa[x] = find1(fa[x]);
 91 }
 92 
 93 void kruskal()
 94 {
 95     sort(e + 1, e + 1 + m, cmp);
 96     for (int i = 1; i <= n; i++) fa[i] = i;
 97 
 98     int tmp = n;
 99     memset(h, -1, sizeof(h));
100     for (int i = 1; i <= m; i++)
101     {
102         int x = e[i].a, y = e[i].b;
103         int fx = find1(x), fy = find1(y);
104         if (fx == fy) continue;
105 
106         fa[fx] = ++tmp;
107         fa[fy] = tmp;
108         fa[tmp] = tmp;
109 
110         add(tmp, fx), add(tmp, fy);
111         F[fx][0] = F[fy][0] = tmp;
112         dis[tmp] = e[i].d;
113     }
114 
115     build(rt[0], 1, lenb);
116     dfs(tmp);
117 }
118 
119 int main()
120 {
121     int q;
122     scanf("%d%d%d", &n, &m, &q);
123     for (int i = 1; i <= n; i++)
124     {
125         scanf("%d", &w[i]);
126         b[i] = w[i];
127     }
128 
129     sort(b + 1, b + n + 1);
130     lenb = unique(b + 1, b + 1 + n) - b - 1;
131 
132     for (int i = 1; i <= m; i++)
133     {
134         int x, y, z;
135         scanf("%d%d%d", &x, &y, &z);
136         e[i] = Edge(x, y, z);
137     }
138 
139     kruskal();
140 
141     while (q--)
142     {
143         int x, d, k;
144         scanf("%d%d%d", &x, &d, &k);
145         for (int i = 18; i >= 0; i--)
146         {
147             if (F[x][i] && dis[F[x][i]] <= d) x = F[x][i];
148         }
149         if (sum[rt[range[x][1]]] - sum[rt[range[x][0]]] < k) puts("-1");
150         else    printf("%d\n", b[query(rt[range[x][0]], rt[range[x][1]], 1, lenb, k)]);
151     }
152 }
View Code

 

【PA2014】Fiolki(题目):

  类似于kurskal重构树,每次混合药剂后,新建一个节点,表示混合后的bi瓶子,新节点分别是ai,bi的父节点,下次需要用到bi来混合时就要用这个新建节点来加边。

反应发生的位置就是会发生反应的两个药剂的LCA,将反应按照LCA深度从大到小为第一关键字,编号从小到大为第二关键字排序,没有LCA的反应踢掉(没有一次混合会发生这个反应)。遍历一遍反应,将反应需要的两种药剂都减去两种药剂中剂量较少的药剂的剂量,答案加上较少药剂的剂量*2。

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 using namespace std;
  6 
  7 const int N = 2000010;
  8 
  9 int n, m;
 10 int w[N], pos[N];
 11 int dep[N], F[N][20];
 12 int h[N], num[N], nex[N], dqx;
 13 
 14 struct Node
 15 {
 16     int a, b, dep, id;
 17     Node() {}
 18     Node(int a, int b, int dep, int id) :a(a), b(b), dep(dep), id(id) {}
 19 };
 20 
 21 Node rea[N];
 22 
 23 bool cmp(Node a, Node b)
 24 {
 25     return a.dep == b.dep ? a.id < b.id : a.dep > b.dep;
 26 }
 27 
 28 void add(int a, int b)
 29 {
 30     num[dqx] = b;
 31     nex[dqx] = h[a];
 32     h[a] = dqx++;
 33 }
 34 
 35 void dfs(int u)
 36 {
 37     dep[u] = dep[F[u][0]] + 1;
 38 
 39     for (int i = 1; i <= 18; i++) F[u][i] = F[F[u][i - 1]][i - 1];
 40 
 41     for (int i = h[u]; ~i; i = nex[i])
 42     {
 43         int j = num[i];
 44         if (j == F[u][0]) continue;
 45         F[j][0] = u;
 46         dfs(j);
 47     }
 48 }
 49 
 50 int LCA(int a, int b)
 51 {
 52     if (dep[a] < dep[b]) swap(a, b);
 53     for (int i = 18; i >= 0; i--)
 54     {
 55         if (dep[F[a][i]] >= dep[b]) a = F[a][i];
 56     }
 57 
 58     if (a == b) return a;
 59 
 60     for (int i = 18; i >= 0; i--)
 61     {
 62         if (F[a][i] != F[b][i])
 63         {
 64             a = F[a][i];
 65             b = F[b][i];
 66         }
 67     }
 68 
 69     return F[a][0];
 70 }
 71 
 72 int main()
 73 {
 74     int q;
 75     scanf("%d%d%d", &n, &m, &q);
 76     for (int i = 1; i <= n; i++)
 77     {
 78         pos[i] = i;
 79         scanf("%d", &w[i]);
 80     }
 81 
 82     memset(h, -1, sizeof(h));
 83     for (int i = 1; i <= m; i++)
 84     {
 85         int a, b;
 86         int c = n + i;
 87         scanf("%d%d", &a, &b);
 88         add(c, pos[a]);
 89         add(c, pos[b]);
 90         pos[b] = c;
 91     }
 92 
 93     for (int i = n + m; i; i--)
 94     {
 95         if (!F[i][0]) dfs(i);
 96     }
 97 
 98     for (int i = 1; i <= q; i++)
 99     {
100         int a, b;
101         scanf("%d%d", &a,&b);
102         int lca = LCA(a, b);
103         if (!lca) continue;
104         rea[i] = Node(a, b, dep[lca], i);
105     }
106 
107     sort(rea + 1, rea + 1 + q, cmp);
108 
109     long long ans = 0;
110     for (int i = 1; i <= q; i++)
111     {
112         int a = rea[i].a, b = rea[i].b;
113         int t = min(w[a], w[b]);
114         ans += t * 2;
115         w[a] -= t, w[b] -= t;
116     }
117 
118     printf("%lld", ans);
119 }
View Code

 

树分治:

  核心:

   用来处理树上所有点对路径的问题,每次递归找出当前子树的重心,经过重心的路径进行统计,该重心统计完后再递归子树进行相同操作。

 

【CF293E】Close Vertices(题目):

  每次递归找出子树的重心,从重心往其他点查询满足的点对数。

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<queue>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 const int N = 2000010;
  9 
 10 int n, l, w;
 11 long long ans;
 12 int t[N], f[N];
 13 int head, tail, sum, root;
 14 int size1[N], dep[N], len[N], dis[N];
 15 int h[N], num[N], nex[N], v[N], dqx;
 16 bool st[N];
 17 
 18 queue<int> que;
 19 
 20 struct Node
 21 {
 22     int l, d;
 23     Node() {}
 24     Node(int l, int d) :l(l), d(d) {}
 25 };
 26 
 27 Node q[N];
 28 
 29 bool cmp1(Node x, Node y)
 30 {
 31     return x.d < y.d;
 32 }
 33 
 34 bool cmp2(Node x, Node y)
 35 {
 36     return x.d > y.d;
 37 }
 38 
 39 void add(int a, int b, int c)
 40 {
 41     num[dqx] = b;
 42     v[dqx] = c;
 43     nex[dqx] = h[a];
 44     h[a] = dqx++;
 45 }
 46 
 47 void get_root(int u, int fa)
 48 {
 49     size1[u] = 1;
 50     f[u] = 0;
 51     for (int i = h[u]; ~i; i = nex[i])
 52     {
 53         int j = num[i];
 54         if ((j == fa) || st[j]) continue;
 55 
 56         get_root(j, u);
 57         size1[u] += size1[j];
 58         f[u] = max(f[u], size1[j]);
 59     }
 60 
 61     f[u] = max(f[u], sum - size1[u]);
 62     if ((f[u] < f[root]) || (!root)) root = u;
 63 }
 64 
 65 int lowbit(int x)
 66 {
 67     return x & -x;
 68 }
 69 
 70 void update(int u, int k)
 71 {
 72     while (u <= n)
 73     {
 74         t[u] += k;
 75         u += lowbit(u);
 76     }
 77 }
 78 
 79 int get(int x)
 80 {
 81     int res = 0;
 82     while (x > 0)
 83     {
 84         res += t[x];
 85         x -= lowbit(x);
 86     }
 87     return res;
 88 }
 89 
 90 void dfs(int u, int fa)
 91 {
 92     size1[u] = 1;
 93     for (int i = h[u]; ~i; i = nex[i])
 94     {
 95         int j = num[i];
 96         if (j == fa || st[j]) continue;
 97 
 98         len[j] = len[u] + 1;
 99         dis[j] = dis[u] + v[i];
100         q[++tail] = Node(len[j], dis[j]);
101 
102         dfs(j, u);
103 
104         size1[u] += size1[j];
105         if (u == root)
106         {
107             sort(q + 1, q + head + 1, cmp1);
108             sort(q + head + 1, q + tail + 1, cmp2);
109             int k = 1;
110             for (int j = head + 1; j <= tail; j++)
111             {
112                 while ((q[k].d + q[j].d <= w) && (k <= head))
113                 {
114                     update(q[k].l + 1, 1);
115                     k++;
116                 }
117                 ans += (long long)get(l - q[j].l + 1);
118             }
119             for (int g = 1; g < k; g++) update(q[g].l + 1, -1);
120             head = tail;
121             que.push(j);
122         }
123     }
124 }
125 
126 int main()
127 {
128     scanf("%d%d%d", &n, &l, &w);
129 
130     memset(h, -1, sizeof(h));
131     for (int i = 2; i <= n; i++)
132     {
133         int x, d;
134         scanf("%d%d", &x, &d);
135         add(i, x, d), add(x, i, d);
136     }
137 
138     que.push(1);
139     size1[1] = n;
140     while (!que.empty())
141     {
142         int u = que.front();
143         que.pop();
144 
145         sum = size1[u];
146         root = 0;
147         get_root(u, 0);
148         len[root] = dis[root] = 0;
149 
150         head = 1, tail = 1;
151         q[1] = Node(0, 0);
152         dfs(root, 0);
153         st[root] = true;
154     }
155     printf("%lld", ans);
156 }
View Code

 

【NOI2014】购票(题目):

  设f[u]为u到1的最小代价,则:

    

  设d[u]=dis[u]×pu+qu,则:

    

 

   若有两个合法的转移点j,k,比较他们转移的优劣:

    

 

   当j比k更优时,则:

    

 

   即点(dis[j],f[j])和点(dis[k],f[k])之间的斜率大于pu,用所以我们需要用单调栈维护一个合法点的下凸壳,这样可以根据不同的 pu 在凸壳上二分得到决策点。维护一些区间中的凸壳,查询时选择一些区间的并恰好等于需要的后缀,在每个区间内分别查询最优决策并更新。用树状数组维护后缀信息

 

 

 

 

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<vector>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 
  8 typedef long long LL;
  9 
 10 const int N = 4000010;
 11 const LL INF = 2e18 + 7;
 12 
 13 int n, faz[N];
 14 LL wgh[N];
 15 LL dep[N], dis[N];
 16 LL p[N], q[N], b[N], len[N];
 17 LL stds[N], tds;
 18 LL f[N], * X = dis, * Y = f;
 19 
 20 std::vector<int> G[N];
 21 
 22 int stkp[N], valp[N], tpp[N];
 23 int* istk[N], * ival[N], * itp[N], it[N];
 24 
 25 double slope(int i, int j) 
 26 {
 27     return X[i] == X[j] ? 1e60 : (double)(Y[j] - Y[i]) / (X[j] - X[i]);
 28 }
 29 
 30 void push(int id, int u) 
 31 {
 32     int t = it[id], tp = itp[id][t], * stk = istk[id];
 33     while (tp > 0 && slope(stk[tp - 1], stk[tp]) >= slope(stk[tp], u)) --tp;
 34     ++t, ival[id][t] = stk[itp[id][t] = ++tp], stk[tp] = u, it[id] = t;
 35 }
 36 
 37 void pop(int id)
 38 {
 39     istk[id][itp[id][it[id]]] = ival[id][it[id]];
 40     --it[id];
 41 }
 42 
 43 int check(int id, LL slp) 
 44 {
 45     int t = it[id], tp = itp[id][t], * stk = istk[id];
 46 
 47     if (!~tp) return -1;
 48     int lb = 0, rb = tp - 1, x = tp, mid;
 49 
 50     while (lb <= rb) 
 51     {
 52         mid = (lb + rb) >> 1;
 53         if (slope(stk[mid], stk[mid + 1]) <= slp) lb = mid + 1;
 54         else x = mid, rb = mid - 1;
 55     }
 56     return stk[x];
 57 }
 58 
 59 void push_all(int i, int u)
 60 {
 61     for (; i <= n; i += i & -i) push(i, u);
 62 }
 63 
 64 void pop_all(int i)
 65 {
 66     for (; i <= n; i += i & -i) pop(i);
 67 }
 68 
 69 void get_DP(int u) 
 70 {
 71     f[u] = INF;
 72     int i = lower_bound(stds + 1, stds + tds + 1, dis[u] - len[u]) - stds;
 73     for (i = n - i + 1; i; i -= i & -i) 
 74     {
 75         int v = check(i, p[u]);
 76         if (~v) f[u] = std::min(f[u], f[v] - dis[v] * p[u] + b[u]);
 77     }
 78 }
 79 
 80 void dfs(int u) 
 81 {
 82     dep[u] = dep[faz[u]] + 1;
 83     dis[u] = dis[faz[u]] + wgh[u];
 84     stds[++tds] = dis[u];
 85     b[u] = dis[u] * p[u] + q[u];
 86 
 87     get_DP(u);
 88     push_all(n - dep[u], u);
 89     for (auto v : G[u]) dfs(v);
 90     pop_all(n - dep[u]);
 91     --tds;
 92 }
 93 
 94 int main()
 95 {
 96     scanf("%d%*d", &n);
 97 
 98     istk[1] = stkp;
 99     itp[1] = tpp;
100     ival[1] = valp;
101     itp[1][it[1] = 0] = -1;
102 
103     for (int i = 2; i <= n; ++i)
104     {
105         int lenl = ((i - 1) & (1 - i)) + 1;
106         istk[i] = istk[i - 1] + lenl;
107         itp[i] = itp[i - 1] + lenl;
108         ival[i] = ival[i - 1] + lenl;
109         itp[i][it[i] = 0] = -1;
110     }
111 
112     for (int i = 2; i <= n; ++i) 
113     {
114         scanf("%d%lld%lld%lld%lld", &faz[i], &wgh[i], &p[i], &q[i], &len[i]);
115         G[faz[i]].push_back(i);
116     }
117 
118     push_all(n, 1);
119     stds[tds = 1] = 0;
120 
121     for (auto u : G[1]) dfs(u);
122     for (int i = 2; i <= n; ++i) printf("%lld\n", f[i]);
123 }
View Code

 

posted on 2020-07-14 17:00  ArrogHie  阅读(243)  评论(0编辑  收藏  举报