区间最深LCA

求编号在区间[l, r]之间的两两lca的深度最大值。

例题

解:口胡几种做法。前两种基于莫队,第三种是启发式合并 + 扫描线,第四种是lct + 线段树。

①:

有个结论就是这个答案一定是点集中DFS序相邻的两个点的lca。于是开个数据结构,以DFS序为key维护点集,找前驱后继,额外用一个数据结构维护所有lca的深度,取最大值即可。外面套莫队就做完了。

实现上这两个数据结构都可以用树状数组。

  1 #include <bits/stdc++.h>
  2 
  3 #define out(a) std::cerr << #a" = " << a << std::endl;
  4 
  5 template <class T> inline void read(T &x) {
  6     x = 0;
  7     char c = getchar();
  8     while(c < '0' || c > '9') c = getchar();
  9     while(c >= '0' && c <= '9') {
 10         x = x * 10 + c - 48;
 11         c = getchar();
 12     }
 13     return;
 14 }
 15 
 16 const int N = 80010;
 17 
 18 struct Edge {
 19     int nex, v;
 20 }edge[N << 1]; int tp;
 21 
 22 int e[N], pos[N], pos2[N], num, num2, fr[N], ans[N], id[N], ST[N << 1][20], d[N], pw[N << 1], n;
 23 /*std::set<int> st; /// save pos 
 24 std::set<int>::iterator it;*/
 25 /*std::multiset<int> Ans;
 26 std::multiset<int>::iterator it2;*/
 27 
 28 namespace Ans {
 29     int ta[N], cnt;
 30     inline void add(int i) {
 31         ++cnt;
 32 //        printf("Ans : add : %d \n", i);
 33         for(; i <= n; i += i & (-i)) {
 34             ta[i]++;
 35         }
 36         return;
 37     }
 38     inline void del(int i) {
 39         --cnt;
 40 //        printf("Ans : del : %d \n", i);
 41         for(; i <= n; i += i & (-i)) {
 42             ta[i]--;
 43         }
 44         return;
 45     }
 46     inline int getMax() {
 47         int ans = 0, k = cnt, t = pw[n];
 48         while(t >= 0) {
 49             if(((ans | (1 << t)) <= n) && ta[ans | (1 << t)] < k) {
 50                 k -= ta[ans | (1 << t)];
 51                 ans |= (1 << t);
 52             }
 53             t--;
 54         }
 55         return ans + 1;
 56     }
 57 }
 58 
 59 namespace ta {
 60     int ta[N], cnt;
 61     inline void add(int i) {
 62         ++cnt;
 63 //        printf("ta : add : %d \n", i);
 64         for(; i <= n; i += i & (-i)) {
 65             ta[i]++;
 66         }
 67         return;
 68     }
 69     inline void del(int i) {
 70         --cnt;
 71 //        printf("ta : del : %d \n", i);
 72         for(; i <= n; i += i & (-i)) {
 73             ta[i]--;
 74         }
 75         return;
 76     }
 77     inline int getKth(int k) {
 78 //        printf("ta : Kth %d : ", k);
 79         int ans = 0, t = pw[n];
 80         while(t >= 0) {
 81             if(((ans | (1 << t)) <= n) && ta[ans | (1 << t)] < k) {
 82                 k -= ta[ans | (1 << t)];
 83                 ans |= (1 << t);
 84             }
 85             t--;
 86         }
 87 //        printf("%d  cnt = %d \n", ans + 1, cnt);
 88         return ans + 1;
 89     }
 90     inline int getSum(int i) {
 91         int ans = 0;
 92         for(; i; i -= i & (-i)) {
 93             ans += ta[i];
 94         }
 95         return ans;
 96     }
 97 }
 98 
 99 struct Ask {
100     int l, r, id;
101     inline bool operator <(const Ask &w) const {
102         if(fr[l] != fr[w.l]) return l < w.l;
103         return r < w.r;
104     }
105 }ask[N];
106 
107 inline void add(int x, int y) {
108     tp++;
109     edge[tp].v = y;
110     edge[tp].nex = e[x];
111     e[x] = tp;
112     return;
113 }
114 
115 void DFS_1(int x, int f) {
116     d[x] = d[f] + 1;
117 //    printf("x = %d \n", x);
118     pos[x] = ++num;
119     id[num] = x;
120     pos2[x] = ++num2;
121     ST[num2][0] = x;
122     for(int i = e[x]; i; i = edge[i].nex) {
123         int y = edge[i].v;
124         if(y == f) continue;
125         DFS_1(y, x);
126         ST[++num2][0] = x;
127     }
128     return;
129 }
130 
131 inline void prework() {
132     register int i, j;
133     for(i = 2; i <= num2; i++) pw[i] = pw[i >> 1] + 1;
134     for(j = 1; j <= pw[num2]; j++) {
135         for(i = 1; i + (1 << j) - 1 <= num2; i++) {
136             if(d[ST[i][j - 1]] < d[ST[i + (1 << (j - 1))][j - 1]])
137                 ST[i][j] = ST[i][j - 1];
138             else 
139                 ST[i][j] = ST[i + (1 << (j - 1))][j - 1];
140          }
141     }
142     return;
143 }
144 
145 inline int lca(int x, int y) {
146     x = pos2[x];
147     y = pos2[y];
148     if(x > y) std::swap(x, y);
149     int t = pw[y - x + 1];
150     if(d[ST[x][t]] < d[ST[y - (1 << t) + 1][t]]) 
151         return ST[x][t];
152     else
153         return ST[y - (1 << t) + 1][t];
154 }
155 
156 inline void add(int x) {
157 //    std::cerr << "------------ add " << x << std::endl;
158     ta::add(pos[x]);
159 //    std::cerr << "111 \n";
160     int rk = ta::getSum(pos[x]);
161 //    std::cerr << "222 \n";
162     int y = 0, z = 0;
163     if(rk != 1) {
164         y = id[ta::getKth(rk - 1)];
165     }
166     if(rk != ta::cnt) {
167         z = id[ta::getKth(rk + 1)];
168     }
169 //    std::cerr << "333 \n";
170 //    out(y); out(z);
171     if(y) Ans::add(d[lca(x, y)]);
172     if(z) Ans::add(d[lca(x, z)]);
173     if(y && z) Ans::del(d[lca(y, z)]);
174     return;
175 }
176 
177 inline void del(int x) {
178 //    std::cerr << "------------ del " << x << std::endl;
179     int rk = ta::getSum(pos[x]);
180     int y = 0, z = 0;
181     if(rk != 1) {
182         y = id[ta::getKth(rk - 1)];
183     }
184     if(rk != ta::cnt) {
185         z = id[ta::getKth(rk + 1)];
186     }
187     if(y) Ans::del(d[lca(x, y)]);
188     if(z) Ans::del(d[lca(x, z)]);
189     if(y && z) Ans::add(d[lca(y, z)]);
190     ta::del(pos[x]);
191     return;
192 }
193 
194 int main() {
195     
196     freopen("lca.in", "r", stdin);
197     freopen("lca.out", "w", stdout);
198     
199     register int i;
200     int m;
201     read(n); read(m); 
202     for(int i = 1, x, y; i < n; i++) {
203         read(x); read(y);
204         add(x, y); add(y, x);
205     }
206     DFS_1(1, 0);
207     prework();
208     int T = n / sqrt(m);
209     for(i = 1; i <= n; i++) {
210         fr[i] = (i - 1) / T + 1;
211     }
212     for(i = 1; i <= m; i++) {
213         read(ask[i].l); read(ask[i].r);
214         ask[i].id = i;
215     }
216     std::sort(ask + 1, ask + m + 1);
217     int l = 1, r = 1; ta::add(1);
218     for(i = 1; i <= m; i++) {
219         /*if(i % 1 == 0) {
220             std::cerr << "i = " << i << std::endl;
221         }*/
222 //        printf("i = %d [%d %d] ask [%d %d] \n", i, l, r, ask[i].l, ask[i].r);
223         while(ask[i].l < l) {
224             add(--l);
225         } 
226         while(r < ask[i].r) {
227             add(++r);
228         }
229         while(l < ask[i].l) {
230             del(l++);
231         }
232         while(ask[i].r < r) {
233             del(r--);
234         }
235 //        printf("Ans = %d \n", Ans::getMax());
236         ans[ask[i].id] = Ans::getMax();
237     }
238     for(i = 1; i <= m; i++) {
239         printf("%d\n", ans[i]);    
240     }
241     return 0;
242 }
代码

②:

换反回滚莫队(只有删除),第一个数据结构换成链表。可以发现每次删掉一些节点然后按照原顺序插回来的话,可以做到O(1)前驱后继。然后第二个数据结构换成值域分块,可以O(1)修改√查询。

③:

这是一个nlog2n的做法。

考虑点x何时会被作为lca,显然就是在合并子树的时候,两个子树中各有一个点被选。

这个启发式一下,枚举小的那个子树,于是对于枚举到的每个点y,在另一棵子树中每个点被选都会导致x成为一次lca。

考虑到查询的编号总是连续的,于是只要在另一个子树中找到y的前驱后继即可。如果别的点和y有贡献,那么前驱和后继也一定有贡献。

于是我们有了O(nlogn)个点对和m个询问,全部按照左端点排序,从大到小枚举左端点,然后把点对加入。

开一个数据结构维护右端点恰为i的答案。于是答案就是一段前缀的最大值,树状数组即可。

  1 #include <bits/stdc++.h>
  2 
  3 using namespace std;
  4 
  5 const int maxn = 80010;
  6 int n,m,head[maxn],to[maxn * 2],nextt[maxn * 2],tot = 1,deep[maxn],cnt,ans[maxn],c[maxn];
  7 set<int> S[maxn];
  8 
  9 struct node
 10 {
 11     int x,y,v;
 12 }e[2000010],q[2000010];
 13 
 14 inline int read()
 15 {
 16     int x = 0;
 17     char ch = getchar();
 18     while (ch < '0' || ch > '9')
 19         ch = getchar();
 20     while (ch >= '0' && ch <= '9')
 21     {
 22         x = (x << 3) + (x << 1) + ch - '0';
 23         ch = getchar();
 24     }
 25     return x;
 26 }
 27 
 28 inline void add(int x,int y)
 29 {
 30     to[tot] = y;
 31     nextt[tot] = head[x];
 32     head[x] = tot++;
 33 }
 34 
 35 inline void Merge(int x,int y)
 36 {
 37     if (S[x].size() < S[y].size())
 38         S[x].swap(S[y]);
 39     for (set<int>::iterator it = S[y].begin(); it != S[y].end(); ++it)
 40     {
 41         int temp = (*it);
 42         S[x].insert(temp);
 43         set<int>::iterator it2 = S[x].find(temp);
 44         if (it2 != S[x].begin())
 45         {
 46             --it2;
 47             ++cnt;
 48             e[cnt].x = (*it2);
 49             e[cnt].y = temp;
 50             e[cnt].v = deep[x];
 51         }
 52         it2 = S[x].find(temp);
 53         ++it2;
 54         if (it2 != S[x].end())
 55         {
 56             ++cnt;
 57             e[cnt].x = temp;
 58             e[cnt].y = (*it2);
 59             e[cnt].v = deep[x];
 60         }
 61     }
 62     S[y].clear();
 63 }
 64 
 65 void dfs(int u,int faa)
 66 {
 67     deep[u] = deep[faa] + 1;
 68     S[u].insert(u);
 69     for (register int i = head[u];i;i = nextt[i])
 70     {
 71         int v = to[i];
 72         if (v == faa)
 73             continue;
 74         dfs(v,u);
 75         Merge(u,v);
 76     }
 77 }
 78 
 79 inline void Add(int x,int v)
 80 {
 81     while (x <= n)
 82     {
 83         c[x] = max(c[x],v);
 84         x += x & (-x);
 85     }
 86 }
 87 
 88 inline int Query(int x)
 89 {
 90     int res = 0;
 91     while (x)
 92     {
 93         res = max(res,c[x]);
 94         x -= x & (-x);
 95     }
 96     return res;
 97 }
 98 
 99 inline bool cmp(node a,node b)
100 {
101     return a.x > b.x;
102 }
103 
104 int main()
105 {
106     freopen("lca.in","r",stdin);
107     freopen("lca.out","w",stdout);
108     n = read(),m = read();
109     for (register int i = 1; i < n; i++)
110     {
111         int x,y;
112         x = read(),y = read();
113         add(x,y);
114         add(y,x);
115     }
116     dfs(1,0);
117     for (register int i = 1; i <= m; i++)
118     {
119         q[i].x = read(),q[i].y = read();
120         q[i].v = i;
121     }
122     sort(q + 1,q + 1 + m,cmp);
123     sort(e + 1,e + 1 + cnt,cmp);
124     int cur = 1;
125     for (register int i = 1; i <= m; i++)
126     {
127         while (cur <= cnt && e[cur].x >= q[i].x)
128         {
129             Add(e[cur].y,e[cur].v);
130             cur++;
131         }
132         ans[q[i].v] = Query(q[i].y);
133     }
134     for (register int i = 1; i <= m; i++)
135         printf("%d\n",ans[i]);
136     
137     return 0;
138 }
代码

可以用树套树做到在线。

④:

这是一个上界nlog²n的做法。参考资料

posted @ 2019-03-21 15:15  huyufeifei  阅读(328)  评论(2编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜