• 树链剖分简析


    落谷3384

      1 #include<cstdio>
      2 #include<cstdlib>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<algorithm>
      6 #include<queue>
      7 #include<stack>
      8 #include<deque>
      9 #include<map>
     10 #include<iostream>
     11 using namespace std;
     12 typedef long long  LL;
     13 const double pi=acos(-1.0);
     14 const double e=exp(1);
     15 //const int MAXN =2e5+10;
     16 const LL N = 100010*4;
     17 
     18 struct node
     19 {
     20     LL l,r;
     21     LL mid() {
     22         return (l + r) / 2;  
     23     }
     24 }node[N];
     25 LL sum[N];
     26 LL add[N];
     27 
     28 LL con[N];
     29 LL head[N];
     30 struct edge{
     31     LL to;
     32     LL next;
     33     LL w;
     34 }edge[N];
     35 
     36 
     37 LL f[N];  //结点i的父结点
     38 LL d[N];   // 结点i的深度
     39 LL size[N]; // 结点i子树的规模(结点数)
     40 LL son[N];  // 结点i的重结点
     41 LL top[N];  // 结点i所在链的顶端结点
     42 LL id[N];   // 树中结点剖分后对应的数组下标
     43 LL rk[N];   // 剖分后数组下标对应的树上的结点
     44 
     45 LL cnt1 = 1, cnt = 0;
     46 LL n,m,r,p;
     47 
     48 void PushUp(LL i)
     49 {
     50     //sum[i] = max(sum[i << 1], sum[i << 1 | 1]);
     51     sum[i] = sum[i << 1] + sum[i << 1 | 1];
     52 }
     53 
     54 void PushDown(LL i,LL L) // LΪÇøŒä³€¶È £šÑÓ³Ù±êŒÇ£©
     55 {
     56     if(add[i])
     57     {
     58         add[i << 1] += add[i] % p;
     59         add[i << 1 | 1] += add[i] % p;
     60         sum[i << 1] += add[i] * (L - (L >> 1)) % p; // Ò׎í
     61         sum[i << 1 | 1] += add[i] * (L >> 1) % p;
     62         add[i] = 0;
     63     }
     64 }
     65 
     66 void Build(LL l, LL r, LL i)
     67 {
     68     //cout << " l: " << l << " r: " << r << endl;
     69     node[i].l = l;
     70     node[i].r = r;
     71     sum[i] = 0;
     72     add[i] = 0;
     73     if(l == r)
     74     {
     75      //   cout << " l: " << l << " r: " << r << " " << rk[cnt1] << endl;
     76         sum[i] = con[rk[cnt1++]];
     77         return ;
     78         //scanf("%d",&sum[i]);
     79     }
     80     LL m = node[i].mid();
     81 
     82     Build(l, m, i << 1);
     83     Build(m+1, r, i << 1 | 1);
     84 
     85     PushUp(i);
     86 }
     87 
     88 LL Query(LL l, LL r, LL i)
     89 {
     90     if(node[i].l == l && node[i].r == r)
     91     {
     92         return sum[i];
     93     }
     94     PushDown(i, node[i].r - node[i].l + 1);
     95     LL m = node[i].mid();
     96     LL ans = 0;
     97     if(r <= m)
     98     {
     99         ans += Query(l, r, i << 1) % p;
    100     }
    101     else
    102     {
    103         if(l > m)
    104         {
    105             ans += Query(l, r, i << 1 | 1) % p;
    106         }
    107         else
    108         {
    109             ans += Query(l, m, i << 1) % p;
    110             ans += Query(m+1, r, i << 1 | 1) % p;
    111         }
    112     }
    113     return ans;
    114 }
    115 
    116 void Update(LL l, LL r, LL x, LL i)
    117 {
    118    // cout << " ^^ " << node[i].l << " " << node[i].r << endl;
    119     if(node[i].l == l && node[i].r == r)
    120     {
    121         sum[i] += x * (r - l + 1)  % p;
    122         add[i] += x % p;
    123         return ;
    124     }
    125     PushDown(i,node[i].r - node[i].l + 1);
    126     LL m = node[i].mid();
    127     if(r <= m)
    128     {
    129         Update(l, r, x, i << 1);
    130     }
    131     else
    132     {
    133         if(l > m)
    134         {
    135             Update(l, r, x, i << 1 | 1);
    136         }
    137         else
    138         {
    139             Update(l, m, x, i << 1);
    140             Update(m+1, r, x, i << 1 | 1);
    141         }
    142     }
    143     PushUp(i);
    144 }
    145 
    146 
    147 
    148 // 处理size,son,f,d数组
    149 void dfs1(LL u, LL fa, LL depth) //当前节点、父节点、层次深度
    150 {
    151     LL i;
    152     f[u] = fa;
    153     d[u] = depth;
    154     size[u] = 1;   //这个点本身size = 1;
    155 
    156     for( i = head[u]; i != -1; i = edge[i].next)
    157     {
    158         LL v = edge[i].to;
    159         if(v == fa)
    160             continue;
    161         dfs1(v, u, depth + 1);
    162         size[u] += size[v];
    163         if(size[v] > size[son[u]]) //son[u]数组为u的重儿子
    164         {
    165             son[u] = v;  
    166         }
    167     }
    168 }
    169 //dfs1(root, 0, 1); // 处理结点子树的规模,每个结点的重儿子,结点所在的深度
    170 
    171 
    172 
    173 // 处理top,id,rk数组
    174 void dfs2(LL u, LL t) //当前结点、重链顶端
    175 {
    176     top[u] = t;
    177     id[u] = ++cnt; //标记dfs序
    178     rk[cnt] = u;    // 序号cnt对应结点u
    179     if(!son[u])     // 当点u为叶子节点时,代表一条重链剖分完毕。
    180         return ;    // 返回到倒数第二个结点,从该结点出发,剖分另一条链。
    181     dfs2(son[u],t);  
    182     
    183     for(LL i = head[u]; i != -1; i = edge[i].next)  //从该结点所连接的结点出发,进行剖分
    184     {
    185         LL v = edge[i].to;
    186         if(v != son[u] && v != f[u])  // 链头要求不能是结点u的重儿子(因为该结点已经被重链占用了)
    187                                       // 也不能是结点u的父结点
    188             dfs2(v,v);        //每条链都是以轻结点为开头,后面链的都是重儿子。所以结点v的链头是它本身。
    189     }                           
    190     
    191 }
    192 
    193 void U_xy(LL x, LL y, LL z)
    194 {
    195     LL ans = 0, fx = top[x], fy = top[y];
    196     while(fx != fy)
    197     {
    198         if(d[fx] >= d[fy])
    199         {
    200             if(id[x] <= id[fx])
    201                 Update(id[x], id[fx], z, 1);
    202             else
    203             {
    204                 Update(id[fx], id[x], z, 1);
    205             }
    206             
    207             x = f[fx];
    208             fx = top[x];
    209         }
    210         else
    211         {
    212             if(id[y] <= id[fy])
    213                 Update(id[y], id[fy], z, 1);
    214             else
    215             {
    216                 Update(id[fy], id[y], z, 1);
    217             }
    218             
    219             y = f[fy];
    220             fy = top[y];
    221         }
    222     }
    223 
    224     if(id[x] <= id[y])
    225     {
    226         Update(id[x], id[y], z, 1);
    227     }
    228     else
    229     {
    230         Update(id[y], id[x], z, 1);
    231     }
    232         
    233 }
    234 
    235 LL Q_xy(LL x, LL y)
    236 {
    237     LL ans = 0, fx = top[x], fy = top[y]; //fx代表x的链头结点,fy同理。
    238     while(fx != fy)
    239     {
    240        // cout << " ^^ " << "x: " << x << " y: " << y << "fx: " << fx << "fy: " << fy << endl;
    241        // cout << id[x] << "  " << id[fx] << " ** " << endl;
    242         if(d[fx] >= d[fy])  // 如果x的深度比y的深度大,所以应让x向上跳
    243         {
    244             if(id[x] <= id[fx])
    245                 ans += Query(id[x], id[fx], 1) % p;   // 因为剖分后每条链上的结点都是线性存储的,所以x到x链头的父节点的距离,为两者之间的距离的差。
    246             else
    247             {
    248                 ans += Query(id[fx], id[x], 1) % p;
    249             }
    250             
    251             x = f[fx];    // x更新为x链头结点的父节点
    252             fx = top[x];  // fx更新为新x结点的链头结点, 然后再去和fy比较,判断x,y是否在同一条链上了。
    253         }
    254         else
    255         {
    256             if(id[y] <= id[fy])
    257                 ans += Query(id[y], id[fy], 1) % p;
    258             else
    259             {
    260                 ans += Query(id[fy], id[y], 1) % p;
    261             }
    262             
    263             y = f[fy];
    264             fy = top[y];
    265         }
    266     }
    267 
    268     if(id[x] <= id[y])
    269     {
    270         ans += Query(id[x], id[y], 1) % p;
    271     }
    272     else
    273     {
    274         ans += Query(id[y], id[x], 1) % p;
    275     }
    276 
    277     return ans % p;
    278 }
    279 
    280 void U_x(LL x, LL z)
    281 {
    282    // cout << "id[x]: " << id[x] << "  size: " << size[x] << endl;
    283    // cout << " ** " << Query(id[x],id[x] + size[x] - 1,1) << endl;;    
    284     Update(id[x], id[x] + size[x] - 1, z, 1);
    285    // cout << " ** " << Query(id[x],id[x] + size[x] - 1,1) << endl;;
    286     
    287 }
    288 
    289 LL Q_x(LL x)
    290 {
    291     return Query(id[x], id[x] + size[x] - 1, 1) % p;
    292 }
    293 
    294 
    295 int main()
    296 {
    297     
    298     LL i,j,a,b,x,y,z;
    299 
    300     scanf("%lld%lld%lld%lld",&n,&m,&r,&p);
    301     for(i = 1; i <= n; i ++)
    302     {
    303         scanf("%lld",&con[i]);
    304     }
    305 
    306     memset(head,-1,sizeof(head));
    307     LL cnt2 = 0;
    308     for(i = 1; i < n; i++)
    309     {
    310         scanf("%lld%lld",&a,&b);
    311         edge[cnt2].to = b;
    312         edge[cnt2].next= head[a];
    313         head[a] = cnt2 ++;
    314 
    315 
    316         edge[cnt2].to = a;
    317         edge[cnt2].next = head[b];
    318         head[b] = cnt2 ++;
    319     }
    320     dfs1(r, 0, 1);
    321     dfs2(r, r);
    322 
    323     
    324     Build(1, n, 1);
    325 
    326 /* 
    327     cout << "** ";
    328     for(i = 1; i <= cnt; i++)
    329     {
    330         cout << rk[i] << " " ;
    331     }
    332     cout << endl;
    333 */
    334     while(m--)
    335     {
    336         scanf("%lld",&j);
    337       //  cout << "j: " << j << endl;
    338         if(j == 1)
    339         {
    340             scanf("%lld%lld%lld",&x,&y,&z);
    341             U_xy(x,y,z);
    342         }
    343         else if(j == 2)
    344         {
    345             scanf("%lld%lld",&x,&y);
    346             printf("%lld
    ", Q_xy(x,y) % p);
    347         }
    348         else if(j == 3)
    349         {
    350             scanf("%lld%lld",&x,&z);
    351             U_x(x,z);
    352         }
    353         else if(j == 4)
    354         {
    355             scanf("%lld",&x);
    356             printf("%lld
    ", Q_x(x) % p);
    357         }
    358     }
    359     return 0;
    360 }
    361 
    362 
    363 
    364 
    365 
    366 
    367 
    368 
    369 /* 
    370  888888888888888888888888888 36375  68190  63528 ** 
    371  888888888888888888888888888 66907  32530  32528 ** 
    372 
    373 
    374   888888888888888888888888888 36375  31713  68190  63528 ** 
    375  888888888888888888888888888 66907  86992  32530  32528 ** 
    376 
    377  */
  • 相关阅读:
    VC combobox
    myitoa()函数
    什么是虚拟显示
    一些itoa()函数
    C语言运算符优先级
    一些小问题
    return 语句会发生的错误
    Sizeof与Strlen的区别与联系
    用异或运算符实现不用第三个临时变量交换两个变量的值
    字符串化运算符#
  • 原文地址:https://www.cnblogs.com/daybreaking/p/11312951.html
Copyright © 2020-2023  润新知