(LCA+树上主席树)FZU 2237 - 中位数

题意:

多次查询一个树链上的中位数(其实就是求K大)。

分析:

感觉莫队可做,只是不会树上莫队。。

而且这里是边权,处理起来貌似有点小麻烦。。

后来发现其实貌似是一个很老的题,,kuangbin模板书上有类似的题。

树链上的第K大数,这是一道可以用主席树解的题,复杂度才nlogn。

这里也是这样先求从根到每个点的线段树,以为树存在父子关系,所有可以从让下层继承上层的线段树,非常符合主席树的可持久化思想。

然后在查询第K大的时候,去掉重复部分,就可以查了。

太强了,,,

代码:

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <algorithm>
  4 #include <cmath>
  5 #include <cstring>
  6 #include <set>
  7 #include <vector>
  8 #include <queue>
  9 #include <map>
 10 #include <list>
 11 #include <bitset>
 12 #include <string>
 13 #include <cctype>
 14 #include <cstdlib>
 15
 16 using namespace std;
 17
 18 typedef long long ll;
 19 typedef unsigned long long ull;
 20 #define inf (0x3f3f3f3f)
 21 #define lnf (0x3f3f3f3f3f3f3f3f)
 22 #define eps (1e-8)
 23 int sgn(double a) {
 24     return a < -eps ? -1 : a < eps ? 0 : 1;
 25 }
 26
 27 int n, q;
 28 const int maxn = 50010;
 29 const int m = 100001;
 30 struct Node {
 31     int v, c;
 32 };
 33
 34 vector<Node>  g[maxn];
 35 const int DEG = 16;
 36 int fa[maxn][DEG];
 37 int deg[maxn];
 38
 39 void addedge(int u, int v, int w) {
 40     Node a, b;
 41     a.v = v;
 42     a.c = w;
 43     b.v = u;
 44     b.c = w;
 45     g[u].push_back(a);
 46     g[v].push_back(b);
 47 }
 48
 49 void bfs(int root) {
 50     queue<int> q;
 51     deg[root] = 0;
 52     fa[root][0] = root;
 53     q.push(root);
 54     while(!q.empty()) {
 55         int tmp = q.front();
 56         q.pop();
 57         for(int i = 1; i < DEG; i++) {
 58             fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1];
 59         }
 60         for(int i = 0; i < (int)g[tmp].size(); i++) {
 61             int v = g[tmp][i].v;
 62             if(v == fa[tmp][0])continue;
 63             deg[v] = deg[tmp] + 1;
 64             fa[v][0] = tmp;
 65             q.push(v);
 66         }
 67     }
 68 }
 69
 70 int LCA(int u, int v) {
 71     if(deg[u] > deg[v])swap(u, v);
 72     for(int det = deg[v] - deg[u], i = 0; det; det >>= 1, i++) {
 73         if(det & 1)v = fa[v][i];
 74     }
 75     if(u == v)return u;
 76     for(int i = DEG - 1; i >= 0; i--) {
 77         if(fa[u][i] != fa[v][i]) {
 78             u = fa[u][i];
 79             v = fa[v][i];
 80         }
 81     }
 82     return fa[u][0];
 83 }
 84
 85
 86
 87
 88 int T[maxn], lson[maxn * 30], rson[maxn * 30];
 89 int c[maxn * 30];
 90 int tot = 0;
 91
 92 int build(int l, int r) {
 93     int root = tot++;
 94     c[root] = 0;
 95     if(l != r) {
 96         int mid = (l + r) >> 1;
 97         lson[root] = build(l, mid);
 98         rson[root] = build(mid + 1, r);
 99     }
100     return root;
101 }
102
103
104 int update(int root, int pos, int val) {
105     int newroot = tot++, tmp = newroot;
106     c[newroot] = c[root] + val;
107     int l = 1, r = m;
108     while(l < r) {
109         int mid = (l + r) >> 1;
110         if(pos <= mid) {
111             lson[newroot] = tot++;
112             rson[newroot] = rson[root];
113             newroot = lson[newroot];
114             root = lson[root];
115             r = mid;
116         } else {
117             rson[newroot] = tot++;
118             lson[newroot] = lson[root];
119             newroot = rson[newroot];
120             root = rson[root];
121             l = mid + 1;
122         }
123         c[newroot] = c[root] + val;
124     }
125     return tmp;
126 }
127
128 int query(int u_root, int v_root, int fa_root, int k) {
129     int l = 1, r = m;
130     while(l < r) {
131         int mid = (l + r) >> 1;
132         if(c[lson[u_root]] + c[lson[v_root]] - 2 * c[lson[fa_root]] >= k) {
133             r = mid;
134             u_root = lson[u_root];
135             v_root = lson[v_root];
136             fa_root = lson[fa_root];
137         } else {
138             l = mid + 1;
139             k -= c[lson[u_root]] + c[lson[v_root]] - 2 * c[lson[fa_root]];
140             u_root = rson[u_root];
141             v_root = rson[v_root];
142             fa_root = rson[fa_root];
143         }
144     }
145     return l;
146 }
147
148
149
150 void dfs(int u, int par, int val) {
151     T[u] = update(T[par], val, 1);
152     for(int i = 0; i < (int)g[u].size(); i++) {
153         int v = g[u][i].v;
154         if(v == par)continue;
155         dfs(v, u, g[u][i].c);
156     }
157 }
158
159
160 void init() {
161     for(int i = 1; i <= n; i++) {
162         g[i].clear();
163     }
164     tot = 0;
165     memset(fa, 0, sizeof(fa));
166 }
167
168 void solve() {
169     while(~scanf("%d%d", &n, &q)) {
170         init();
171         for(int i = 0; i < n - 1; i++) {
172             int u, v, w;
173             scanf("%d%d%d", &u, &v, &w);
174             w++;
175             addedge(u, v, w);
176
177         }
178         bfs(1);
179         T[0] = build(1, m);
180         dfs(1, 0, 1);
181         while(q--) {
182             int u, v;
183             scanf("%d%d", &u, &v);
184             int fa = LCA(u, v);
185             int k = (c[T[u]] + c[T[v]] - 2 * c[T[fa]] + 1) / 2;
186             printf("%d\n", query(T[u], T[v], T[fa], k) - 1);
187         }
188
189     }
190
191 }
192
193
194
195 int main() {
196
197 #ifndef ONLINE_JUDGE
198 //    freopen("1.in", "r", stdin);
199     //freopen("1.out", "w", stdout);
200 #endif
201     //iostream::sync_with_stdio(false);
202     solve();
203     return 0;
204 }
时间: 2024-10-25 09:33:47

(LCA+树上主席树)FZU 2237 - 中位数的相关文章

SPOJ COT Count on a tree(树上主席树 + LCA 求路径第k小)题解

题意:n个点的树,每个点有权值,问你u~v路径第k小的点的权值是? 思路: 树上主席树就是每个点建一棵权值线段树,具体看JQ博客,LCA用倍增logn求出,具体原理看这里 树上主席树我每个点的存的是点u到源点1的权值线段树,那我求点u到v的所有点,显然是 u + v - lca - fa[lca],就是u到1 + v到1 - 多算的lca - 多算的fa[lca].不能减去两个lca不然少一个点了, LCA板子: //LCA int fa[maxn][20]; int dep[maxn]; vo

p3302 [SDOI2013]森林(树上主席树+启发式合并)

对着题目yy了一天加上看了一中午题解,终于搞明白了我太弱了 连边就是合并线段树,把小的集合合并到大的上,可以保证规模至少增加一半,复杂度可以是\(O(logn)\) 合并的时候暴力dfs修改倍增数组和维护主席树即可 然后树上主席树就是维护节点到根节点的信息即可, 询问链上的第k大时,画图后可以发现维护一个rootx,rooty,rootlca和rootfalca即可解决问题 注意空间要开够 然后注意细节,没了 #include <cstdio> #include <cstring>

BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]

2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 5217  Solved: 1233[Submit][Status][Discuss] Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. Input 第一

P2633 Count on a tree(树上主席树)

思路 运用树上差分的思想,转化成一个普通的主席树模型即可求解 代码 #include <cstdio> #include <algorithm> #include <cstring> using namespace std; struct Node{ int lson,rson,sz; }pt[100100*30]; const int MAXlog=19; int dep[100100],jump[100100][MAXlog],lastans=0,n,m,u[100

P2633 Count on a tree 树上主席树

题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. 输入格式 第一行两个整数N,M. 第二行有N个整数,其中第i个整数表示点i的权值. 后面N-1行每行两个整数(x,y),表示点x到点y有一条边. 最后M行每行两个整数(u,v,k),表示一组询问. 输出格式 M行,表示每个询问的答案. 输入输出样例 输入 #1复制 8 5 105

【树上主席树】BZOJ2588-Count on a tree

[题目大意] 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文. [思路] 这道题迷之好写,因为思路条理太清晰了! 我们每个点就是一棵线段树,维护它到根的每个数字的个数,但是这样会MLE所以自然而然地用主席树来维护. u->v路径上每种的个数就等于sum[u]-sum[lca(u,v)]+sum[v]-sum[fa[lca(u,v)]]. 写

FZU 2237 中位数 主席树 树上k大

#include <cstdio> #include <cstring> #include <queue> #include <set> #include <stack> #include <cstdlib> #include <algorithm> #include <time.h> #include <vector> #include <cmath> using namespace

[SDOI2013]森林(树上主席树)

[SDOI2013]森林(luogu) Description 题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,

#LOJ2564 SDOI2018 原题识别 主席树

转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/9057297.html 原题链接: 今天考试考了前天的SDOI考题 天啊我菜爆,只有T2拿了30分 然后考试后半程一直在打T1 觉得考试思路很有意思,于是就顺着打下来了 个人感觉这个是$O(nlogn^{2})$的,但是在loj上我比claris的程序快了1s多,只不过编程复杂度不止翻倍啊…… 下面介绍一下我的解法 其实最早启发我的是链上的部分分 定义$pre_{i}$为i前面最近的和i同色的点的下标,我们把