zoukankan      html  css  js  c++  java
  • 区间最深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 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ", 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 
    ";
    160     int rk = ta::getSum(pos[x]);
    161 //    std::cerr << "222 
    ";
    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 
    ";
    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] 
    ", 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 
    ", Ans::getMax());
    236         ans[ask[i].id] = Ans::getMax();
    237     }
    238     for(i = 1; i <= m; i++) {
    239         printf("%d
    ", 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
    ",ans[i]);
    136     
    137     return 0;
    138 }
    代码

    可以用树套树做到在线。

    ④:

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

  • 相关阅读:
    【Python】Visual Studio Code 安装&&使用 hello python~~~~
    linux系统644、755、777权限详解
    python对象销毁(垃圾回收)
    linux .bashrc文件修改和生效
    Python学习笔记(20)-文件和文件夹的移动、复制、删除、重命名
    HDU 4635 Strongly connected(强连通)经典
    推荐一款免费的SQLsever的备份软件sqlBackupAndFtp
    Objective-C基础笔记(6)Block
    iOS开发UI篇—懒载入
    关于Adapter对数据库的查询、删除操作
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10571871.html
Copyright © 2011-2022 走看看