洛谷P3302 森林

题意:给定森林,可以把两棵树连起来或者询问链上第k大。

解:启发式合并。

我一开始想到了启发式合并但是发现这样做之后一棵子树就不是一段连续的区间了,那就不能子树xxx了,很迷惘。

后来看了题解发现本来就不需要子树是连续区间......

每次把小的树暴力DFS重构fa[][]和重建主席树。

调了半天是因为lastans没有套上X[]......

注意并查集merge的时候可能有元素为0。无视之即可。

  1 #include <cstdio>
  2 #include <algorithm>
  3 
  4 const int N = 80010, M = 30000010;
  5 
  6 struct Edge {
  7     int nex, v;
  8 }edge[N << 1]; int top;
  9 
 10 int X[N], e[N], n, val[N], temp, pw[N], fa[N][20], tot, vis[N], father[N], siz[N], d[N], rt[N];
 11 int sum[M], ls[M], rs[M];
 12 char str[3];
 13 
 14 int find(int x) {
 15     if(father[x] == x) {
 16         return x;
 17     }
 18     return father[x] = find(father[x]);
 19 }
 20 
 21 inline void merge(int x, int y) {
 22     if(!x || !y) {
 23         return;
 24     }
 25     x = find(x);
 26     y = find(y);
 27     if(x == y) {
 28         return;
 29     }
 30     father[x] = y;
 31     siz[y] += siz[x];
 32     return;
 33 }
 34 
 35 inline bool check(int x, int y) {
 36     return find(x) == find(y);
 37 }
 38 
 39 inline void add(int x, int y) {
 40     top++;
 41     edge[top].v = y;
 42     edge[top].nex = e[x];
 43     e[x] = top;
 44     return;
 45 }
 46 
 47 void add(int x, int &y, int p, int l, int r) {
 48     if(!y || y == x) {
 49         y = ++tot;
 50         sum[y] = sum[x];
 51         ls[y] = ls[x];
 52         rs[y] = rs[x];
 53     }
 54     if(l == r) {
 55         sum[y]++;
 56         return;
 57     }
 58     int mid = (l + r) >> 1;
 59     if(p <= mid) {
 60         add(ls[x], ls[y], p, l, mid);
 61     }
 62     else {
 63         add(rs[x], rs[y], p, mid + 1, r);
 64     }
 65     sum[y] = sum[ls[y]] + sum[rs[y]];
 66     return;
 67 }
 68 
 69 void DFS(int x, int f) {
 70     merge(x, f);
 71     vis[x] = 1;
 72     fa[x][0] = f;
 73     d[x] = d[f] + 1;
 74     for(int j = 1; j <= pw[n]; j++) {
 75         fa[x][j] = fa[fa[x][j - 1]][j - 1];
 76     }
 77     rt[x] = 0;
 78     add(rt[f], rt[x], val[x], 1, temp);
 79     for(int i = e[x]; i; i = edge[i].nex) {
 80         int y = edge[i].v;
 81         if(y == f) {
 82             continue;
 83         }
 84         DFS(y, x);
 85     }
 86     return;
 87 }
 88 
 89 inline void link(int x, int y) {
 90     if(check(x, y)) {
 91         printf("E1");
 92         exit(0);
 93     }
 94     if(siz[find(x)] < siz[find(y)]) {
 95         std::swap(x, y);
 96     }
 97     DFS(y, x);
 98     add(x, y);
 99     add(y, x);
100     return;
101 }
102 
103 inline int lca(int x, int y) {
104     if(d[x] > d[y]) {
105         std::swap(x, y);
106     }
107     int t = pw[n];
108     while(t >= 0 && d[x] != d[y]) {
109         if(d[fa[y][t]] >= d[x]) {
110             y = fa[y][t];
111         }
112         t--;
113     }
114     if(x == y) {
115         return x;
116     }
117     t = pw[n];
118     while(t >= 0 && fa[x][0] != fa[y][0]) {
119         if(fa[x][t] != fa[y][t]) {
120             x = fa[x][t];
121             y = fa[y][t];
122         }
123         t--;
124     }
125     return fa[x][0];
126 }
127 
128 int Ask(int x, int y, int z, int w, int k, int l, int r) {
129     if(l == r) {
130         return r;
131     }
132     int mid = (l + r) >> 1, s = 0;
133     s = sum[ls[x]] + sum[ls[y]] - sum[ls[z]] - sum[ls[w]];
134     if(k <= s) {
135         return Ask(ls[x], ls[y], ls[z], ls[w], k, l, mid);
136     }
137     else {
138         return Ask(rs[x], rs[y], rs[z], rs[w], k - s, mid + 1, r);
139     }
140 }
141 
142 inline int ask(int x, int y, int k) {
143     if(!check(x, y)) {
144         printf("E2");
145         exit(0);
146     }
147     int z = lca(x, y);
148     if(d[x] + d[y] - d[z] - d[z] + 1 < k) {
149         printf("E3");
150         exit(0);
151     }
152     return Ask(rt[x], rt[y], rt[z], rt[fa[z][0]], k, 1, temp);
153 }
154 
155 int main() {
156 
157     //freopen("in.in", "r", stdin);
158     //freopen("my.out", "w", stdout);
159 
160     int m, q;
161     scanf("%d", &n);
162     scanf("%d%d%d", &n, &m, &q);
163     for(int i = 1; i <= n; i++) {
164         scanf("%d", &val[i]);
165         X[++temp] = val[i];
166         siz[i] = 1;
167         father[i] = i;
168     }
169     for(int i = 1, x, y; i <= m; i++) {
170         scanf("%d%d", &x, &y);
171         add(x, y);
172         add(y, x);
173     }
174     for(int i = 2; i <= n; i++) {
175         pw[i] = pw[i >> 1] + 1;
176     }
177     std::sort(X + 1, X + temp + 1);
178     temp = std::unique(X + 1, X + temp + 1) - X - 1;
179     for(int i = 1; i <= n; i++) {
180         val[i] = std::lower_bound(X + 1, X + temp + 1, val[i]) - X;
181     }
182     for(int i = 1; i <= n; i++) {
183         if(!vis[i]) {
184             DFS(i, 0);
185         }
186     }
187     /// build
188 
189     int lastans = 0;
190     for(int i = 1, x, y, k; i <= q; i++) {
191         scanf("%s%d%d", str, &x, &y);
192         if(str[0] == 'L') { // link
193             link(x ^ lastans, y ^ lastans);
194         }
195         else {
196             scanf("%d", &k);
197             lastans = X[ask(x ^ lastans, y ^ lastans, k ^ lastans)];
198             printf("%d\n", lastans);
199         }
200     }
201 
202     return 0;
203 }
AC代码

 

转载于:https://www.cnblogs.com/huyufeifei/p/10324992.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值