• [LNOI2014]LCA


    ~~~题面~~~

    题解:

      首先我们要转化一下,因为直接求不好求。首先考虑一个点对z的贡献,观察这么一个图:

      显然点x对点z的贡献为2,因为LCA的深度为2。LCA可以看做点x和点z分别走向root的两条路径中第一个重合的点,因此,如果我们给x到root的路径上的点都赋1的点权,那么再从z往上走,

      因为LCA是两条路径中第一个重合的点,因此我们会从LCA开始获取点权,那么走到root后得到的点权和刚好就是点x对点z的贡献!

      因此我们直接做一个树链剖分,然后用线段树来维护区间加和区间查询即可。

      然而直接对每个询问都这么做显然是会超时的,因此我们要考虑一些效率更高的做法。

      因为每个询问都是一个区间的查询,而且符合区间减法,即对于同一点而言ans[1, r] - ans[1, l -1] = ans[l, r]。

      这时思路就很明确了,我们可以从1开始枚举,依次进行区间加,然后每次加了之后就处理一遍相关的询问,跟tarjan求LCA的思想有一点点类似。

      获取了ans[1, r] 和 ans[1, l - 1]后就可以很方便的求出ans[l, r]了。


      

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define R register int
      4 #define AC 50100
      5 #define ac 500100
      6 #define mod 201314
      7 #define getchar() *o++
      8 char READ[5000100], *o = READ;
      9 int n, m, cnt, rnt;
     10 int Head[AC], Next[AC], date[AC], all;
     11 int Size[AC], son[AC], father[AC], dep[AC], ans[AC], top[AC], id[AC], d[AC], last[AC];
     12 
     13 struct node{
     14     int Head[AC], Next[AC*2], date[AC*2], id[AC*2], tot;//error!!!询问会存2次啊,左端点和右端点啊,,,数组开两倍啊!!!
     15     void add(int f, int w, int S)//存询问
     16     {
     17         date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, id[tot] = S;
     18     }
     19 }E;
     20 
     21 struct _question{
     22     int l, r, x;
     23 }q[AC];
     24 
     25 inline void add(int f, int w)
     26 {
     27     date[++all] = w, Next[all] = Head[f], Head[f] = all;
     28 }
     29 
     30 inline int read()
     31 {
     32     int x = 0;char c = getchar();
     33     while(c > '9' || c < '0') c = getchar();
     34     while(c >= '0' && c <= '9') x = x * 10 + c -'0', c = getchar();
     35     return x;
     36 }
     37 
     38 void pre()
     39 {
     40     int a;
     41     n = read(), m = read();
     42     for(R i = 2; i <= n; i++)
     43     {
     44         a = read() + 1;//编号从移到1开始算
     45         add(a, i);
     46         father[i] = a;
     47     }
     48     for(R i = 1; i <= m; i++)
     49     {
     50         q[i].l = read() + 1, q[i].r = read() + 1, q[i].x = read() + 1;
     51         if(q[i].l > q[i].r) swap(q[i].l, q[i].r);
     52         E.add(q[i].l - 1, q[i].x, i);//从前向后枚举点x差分,这样就会先遇到左端点,
     53         E.add(q[i].r, q[i].x, i);//所以遇到右端点查询的时候就直接减就可以了
     54     }
     55     dep[1] = 1;
     56 }
     57 
     58 void dfs1(int x)//get Size, son, dep
     59 {
     60     int now, maxn = 0;
     61     Size[x] = 1;//先加上自己
     62     for(R i = Head[x]; i; i = Next[i])
     63     {
     64         now = date[i];
     65         if(now == father[x]) continue;
     66         dep[now] = dep[x] + 1;
     67         dfs1(now);
     68         Size[x] += Size[now];
     69         if(Size[now] > Size[maxn]) maxn = now;
     70     }
     71     son[x] = maxn;
     72 }
     73 
     74 void dfs2(int x, int topx)//get id, top
     75 {
     76     int now;
     77     id[x] = ++cnt, top[x] = topx;
     78     if(!son[x]) return ;
     79     dfs2(son[x], topx);//重链需要继承
     80     last[x] = son[x];
     81     for(R i = Head[x]; i; i = Next[i])
     82     {
     83         now = date[i];
     84         if(now == father[x] || now == son[x]) continue;//如果是父亲or重儿子就跳过
     85         dfs2(now, now);//新开链
     86     }
     87 }
     88 
     89 int tree[ac], lazy[ac], l[ac], r[ac];
     90 
     91 void build(int x, int ll, int rr)
     92 {
     93     if(ll == rr)
     94     {
     95         l[x] = r[x] = ll;
     96         return ;
     97     }
     98     l[x] = ll, r[x] = rr;
     99     int mid = (ll + rr) >> 1;
    100     build(x * 2, ll, mid);
    101     build(x * 2 + 1, mid + 1, rr);
    102 
    103 }
    104 
    105 inline void pushdown(int x)
    106 {
    107     if(lazy[x])
    108     {
    109         int ll = x * 2, rr = ll + 1;
    110         lazy[ll] += lazy[x], lazy[rr] += lazy[x];
    111         tree[ll] += lazy[x] * (r[ll] - l[ll] + 1) , tree[rr] += lazy[x] * (r[rr] - l[rr] + 1);
    112         lazy[x] = 0;
    113     }
    114 }
    115 
    116 inline void update(int x)
    117 {
    118     tree[x] = tree[x * 2] + tree[x * 2 + 1];
    119 }
    120 
    121 void add(int x, int ll, int rr)//区间加
    122 {
    123     pushdown(x);
    124     if(l[x] == ll && r[x] == rr)
    125     {
    126         tree[x] += (rr - ll + 1);
    127         lazy[x] += 1;
    128         return ;
    129     }
    130     int mid = (l[x] + r[x]) >> 1;
    131     if(rr <= mid) add(x * 2, ll, rr);
    132     else if(ll > mid) add(x * 2 + 1, ll, rr);
    133     else 
    134     {
    135         add(x * 2, ll, mid);
    136         add(x * 2 + 1, mid + 1, rr);
    137     }
    138     update(x);
    139 }
    140 
    141 void search(int x, int ll, int rr)//查询
    142 {
    143     pushdown(x);
    144     if(l[x] == ll && r[x] == rr)
    145     {
    146         rnt += tree[x];
    147         return ;
    148     }
    149     int mid = (l[x] + r[x]) >> 1;
    150     if(rr <= mid) search(x * 2, ll, rr);
    151     else if(ll > mid) search(x * 2 + 1, ll, rr);
    152     else 
    153     {
    154         search(x * 2, ll, mid);
    155         search(x * 2 + 1, mid + 1, rr);
    156     }    
    157 }
    158 
    159 void change(int x)
    160 {
    161     while(x)
    162     {
    163         add(1, id[top[x]], id[x]);
    164         x = father[top[x]];
    165     }
    166     if(x == 1) add(1, 1, 1);//如果不是0就会跳过1
    167 
    168 }
    169 
    170 int find(int x)
    171 {
    172     rnt = 0;
    173     while(x)
    174     {
    175         search(1, id[top[x]], id[x]);
    176         x = father[top[x]];
    177     }
    178     if(x == 1) search(1, 1, 1); 
    179     return rnt;
    180 }
    181 
    182 void work()
    183 {
    184     int x;
    185     for(R i = 1; i <= n; i++)
    186     {
    187         change(i);//修改i ---> root
    188         for(R j = E.Head[i]; j; j = E.Next[j])
    189         {
    190             x = E.date[j];//获取z
    191             ans[E.id[j]] = (find(x) - ans[E.id[j]]) % mod;
    192         }
    193     }
    194     for(R i = 1; i <= m; i++) printf("%d
    ", ans[i]);
    195 }
    196 
    197 int main()
    198 {
    199 //    freopen("in.in", "r", stdin);
    200     fread(READ, 1, 5000000, stdin);
    201     pre();
    202     dfs1(1);
    203     dfs2(1, 1);
    204     build(1, 1, n);
    205     work();
    206 //    fclose(stdin);
    207     return 0;
    208 }
    View Code

     

        

  • 相关阅读:
    smobiler仿京东app搜索页面
    搜索界面设计
    smobiler仿饿了么app筛选页面
    Smobiler低功耗蓝牙连接的过程和参数的含义
    smobiler仿饿了么app搜索页面
    内存分配的几个函数的简单对比分析
    Android仿IOS选择拍照相册底部弹出框
    正则表达式 : 6~16位字符,至少包含数字.字母.符号中的2种
    android 音量键调节无效问题
    windows查看系统过期时间
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9614645.html
Copyright © 2020-2023  润新知