zoukankan      html  css  js  c++  java
  • bzoj 2631: tree

    Time Limit: 30 Sec  Memory Limit: 128 MB
    Submit: 2928  Solved: 975
    [Submit][Status][Discuss]

    Description

     一棵n个点的树,每个点的初始权值为1。对于这棵树有q个操作,每个操作为以下四种操作之一:
    + u v c:将u到v的路径上的点的权值都加上自然数c;
    - u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树;
    * u v c:将u到v的路径上的点的权值都乘上自然数c;
    / u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数。

    Input

      第一行两个整数n,q
      接下来n-1行每行两个正整数u,v,描述这棵树
      接下来q行,每行描述一个操作

    Output

      对于每个/对应的答案输出一行

    Sample Input

    3 2
    1 2
    2 3
    * 1 3 4
    / 1 1

    Sample Output

    4

    HINT

    数据规模和约定

      10%的数据保证,1<=n,q<=2000

      另外15%的数据保证,1<=n,q<=5*10^4,没有-操作,并且初始树为一条链

      另外35%的数据保证,1<=n,q<=5*10^4,没有-操作

      100%的数据保证,1<=n,q<=10^5,0<=c<=10^4

    题解:

      四种操作显然要用动态树来维护,对于+和*的操作,需要打标记,但是在标记下放的时候需要考虑先放+还是先放*的问题。

      假设现在树中有权值为x和权值为y的两个节点,y是x的父亲节点,要对它俩依次进行+a1 *b1 *b2 +a2这四种操作,由于y是x的父亲,所以只是对y打上标记,不会下放到x节点。x节点应该的值是:(x+a1)*b1*b2+a2=a1*b1*b2*x+a1*b1*b2+a2,令t1=a1*b1*b2 , t2=a1*b1*b2+a2,则原式可以写成:t1*x+t2 , 在这里t1即为乘法标记,t2即为加法标记,下放的方式就是 t1*x+t2的形式,从上面的式子可以看出,对于乘法标记了累计原则就是有加有乘,对于加法标记的原则是只乘不加。由此下放标记

      其余的就是动态树的常规操作。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstdlib>
      4 #include<cstring>
      5 #include<cmath>
      6 #include<algorithm>
      7 #include<queue>
      8 #include<vector>
      9 using namespace std;
     10 typedef unsigned int UI;
     11 const UI maxn=200000;
     12 const UI mod=51061;
     13 UI N,Q;
     14 UI c[maxn][2],fa[maxn],key[maxn],size[maxn],sum[maxn],add[maxn],mul[maxn],st[maxn];
     15 char s[10];
     16 bool rev[maxn];
     17 bool isroot(UI x){
     18     return x!=c[fa[x]][0]&&x!=c[fa[x]][1];
     19 }
     20 void calc(UI x,UI a,UI b){//节点x,进行ax+b操作 
     21     if(x==0) return;
     22     key[x]=((key[x]*a)%mod+b)%mod;
     23     sum[x]=((sum[x]*a)%mod+(b*size[x])%mod)%mod;
     24     add[x]=((add[x]*a)%mod+b)%mod;
     25     mul[x]=(mul[x]*a)%mod;
     26 }
     27 void pushup(UI x){
     28     size[x]=size[c[x][0]]+size[c[x][1]]+1;
     29     sum[x]=(sum[c[x][0]]+sum[c[x][1]]+key[x])%mod; 
     30 }
     31 void pushdown(UI x){
     32     UI l=c[x][0],r=c[x][1];
     33     if(rev[x]==true){
     34         rev[x]^=1; rev[l]^=1; rev[r]^=1;
     35         swap(c[x][0],c[x][1]);
     36     }
     37     UI tmpa=add[x],tmpm=mul[x];
     38     add[x]=0; mul[x]=1;
     39     if(tmpa!=0||tmpm!=1){
     40         calc(l,tmpm,tmpa); calc(r,tmpm,tmpa);
     41     }
     42 }
     43 void rotate(UI x){
     44     UI y=fa[x],z=fa[y],l,r;
     45     if(x==c[y][0]) l=0;else l=1; r=l^1;
     46     if(!isroot(y)){
     47         if(y==c[z][0]) c[z][0]=x;
     48         else c[z][1]=x;
     49     }
     50     fa[x]=z; fa[y]=x; fa[c[x][r]]=y;
     51     c[y][l]=c[x][r]; c[x][r]=y;
     52     pushup(y); pushup(x);
     53 }
     54 void splay(UI x){
     55     UI top=0; st[++top]=x;
     56     for(UI i=x;!isroot(i);i=fa[i]){
     57         st[++top]=fa[i];
     58     }
     59     for(UI i=top;i;i--) pushdown(st[i]);
     60     while(!isroot(x)){
     61         UI y=fa[x],z=fa[y];
     62         if(!isroot(y)){
     63             if((x==c[y][0]&&y==c[z][0])||(x==c[y][1]&&y==c[z][1])){
     64                 rotate(y),rotate(x);
     65             }
     66             else rotate(x),rotate(x);
     67         }
     68         else rotate(x);
     69     }
     70 }
     71 void access(UI x){
     72     UI t=0;
     73     while(x){
     74         splay(x);
     75         c[x][1]=t; pushup(x);
     76         t=x; x=fa[x];
     77     }
     78 }
     79 void rever(UI x){
     80     access(x); splay(x); rev[x]^=1;
     81 }
     82 void cut(UI x,UI y){
     83     rever(x); access(y); splay(y); c[y][0]=fa[x]=0; 
     84 }
     85 void link(UI x,UI y){
     86     rever(x); fa[x]=y; splay(x);
     87 }
     88 int main(){
     89     scanf("%u%u",&N,&Q);
     90     for(UI i=1;i<=N;i++) key[i]=sum[i]=size[i]=mul[i]=1;
     91     for(UI i=1,u,v;i<=N-1;i++){
     92         scanf("%u%u",&u,&v);
     93         link(u,v);
     94     }
     95     for(UI i=1;i<=Q;i++){
     96         UI u1,v1,u2,v2,cc;
     97         scanf("%s",s);
     98         if(s[0]=='+'){
     99             scanf("%u%u%u",&u1,&v1,&cc);
    100             rever(u1); access(v1); splay(v1);
    101             calc(v1,1,cc);
    102         }
    103         else if(s[0]=='-'){
    104             scanf("%u%u%u%u",&u1,&v1,&u2,&v2);
    105             cut(u1,v1); link(u2,v2);
    106         }
    107         else if(s[0]=='*'){
    108             scanf("%u%u%u",&u1,&v1,&cc);
    109             rever(u1); access(v1); splay(v1);
    110             calc(v1,cc,0);
    111         }
    112         else{
    113             scanf("%u%u",&u1,&v1);
    114             rever(u1); access(v1); splay(v1);
    115             printf("%u
    ",sum[v1]);
    116         }
    117     }
    118     return 0;
    119 }
  • 相关阅读:
    PowerDesigner 找不到Identity列的解决方法
    C# DataTable 和List之间相互转换的方法
    解决Win8无法升级.NET Framework 3.5.1 提示错误0x800F0906
    C#虚方法和抽象方法区别
    VS自带WCF测试客户端
    asp.net读取Excel数据
    输出用户的IP地址,并且判断用户的IP地址是否在192.168.1.100 --- 192.168.1.150之间
    验证电子邮箱正则表达式
    用PHP实现冒泡排序将数组$a=array()按照从小到大的方式排序
    打开a.txt文件在文件中最前面加上hello
  • 原文地址:https://www.cnblogs.com/CXCXCXC/p/5193300.html
Copyright © 2011-2022 走看看