bzoj2631 tree(路径权值的加乘操作)

来源:互联网 发布:电视网络播放量排行榜 编辑:程序博客网 时间:2024/06/07 02:43

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



分析:
LCT的经典操作+扩展操作
题目中比较难处理的就是路径权值的更改
我们起一次接触同时乘除的操作,是在线段树
实际上这道题就可以运用这个思想:
用两个数组分别记录加(ad)乘(mul)标记,
每次在更新一条路径的时候,我们就像询问时一样,把整条路径的信息都转换到一个点上
这样操作就简化成了点操作:

sum=sum*mul+size*ad

v=v*mul+ad

难度在于标记下传:
首先我们要维护儿子结点的加乘标记

mul[ch]=mul[ch]*mul[fa]

ad[ch]=ad[ch]*mul[fa]+ad[fa]

对于值的修改可以浓缩成这个函数:

void cal(int x,ll a,ll b)    //a乘标记,b加标记{    if (!x) return;    v[x]=(v[x]*a+b)%mod;    sum[x]=(sum[x]*a+size[x]*b)%mod;    ad[x]=(ad[x]+b)%mod;    mul[x]=(mul[x]*a)%mod;}

tip

每个点的初始权值为1
在维护sum的同时,不要忘了维护v

数据要求开unsigned int
我偷了个懒,直接开ll,但是一开始一直是T,我觉得应该是和ll有关,改成了unsigned int就A了

不要忘了取%

为了降低代码复杂度

在push,add和multi的时候都可以调用cal函数

//这里写代码片#include<cstdio>#include<cstring>#include<iostream>#define ll unsigned intusing namespace std;const ll mod=51061;const int N=100010;int ch[N][2],pre[N],q[N],n,Q;ll v[N],sum[N],ad[N],mul[N],size[N];bool rev[N];int get(int bh){    return (ch[pre[bh]][0]==bh? 0:1);}int isroot(int bh){    return ch[pre[bh]][0]!=bh&&ch[pre[bh]][1]!=bh;}void update(int bh){    if (!bh) return;    sum[bh]=v[bh];    if (ch[bh][0]) sum[bh]+=sum[ch[bh][0]];    if (ch[bh][1]) sum[bh]+=sum[ch[bh][1]];    sum[bh]%=mod;    size[bh]=1;    if (ch[bh][0]) size[bh]+=size[ch[bh][0]];    if (ch[bh][1]) size[bh]+=size[ch[bh][1]];    size[bh]%=mod;}void cal(int x,ll a,ll b)    //a乘标记,b加标记{    if (!x) return;    v[x]=(v[x]*a+b)%mod;    sum[x]=(sum[x]*a+size[x]*b)%mod;    ad[x]=(ad[x]*a+b)%mod;    mul[x]=(mul[x]*a)%mod;}void push(int bh){    if (!bh) return;    if (rev[bh])     {        if (ch[bh][0]) rev[ch[bh][0]]^=1;        if (ch[bh][1]) rev[ch[bh][1]]^=1;        rev[bh]^=1;        swap(ch[bh][0],ch[bh][1]);    }    if (ad[bh]!=0||mul[bh]!=1)    {        cal(ch[bh][0],mul[bh],ad[bh]);        cal(ch[bh][1],mul[bh],ad[bh]);    }    mul[bh]=1; ad[bh]=0;}void rotate(int bh){    int fa=pre[bh];    int grand=pre[fa];    int wh=get(bh);    if (!isroot(fa)) ch[grand][ch[grand][0]==fa? 0:1]=bh;    pre[bh]=grand;    ch[fa][wh]=ch[bh][wh^1];    pre[ch[fa][wh]]=fa;    ch[bh][wh^1]=fa;    pre[fa]=bh;    update(fa);    update(bh);}void splay(int bh){    int top=0;    q[++top]=bh;    for (int i=bh;!isroot(i);i=pre[i])        q[++top]=pre[i];    while (top) push(q[top--]);    for (int fa;!isroot(bh);rotate(bh))        if (!isroot(fa=pre[bh]))            rotate(get(fa)==get(bh)? fa:bh);}void expose(int bh){    int t=0;    while (bh)    {        splay(bh);        ch[bh][1]=t;        update(bh);    //        t=bh;        bh=pre[bh];    }}void makeroot(int bh){    expose(bh);    splay(bh);    rev[bh]^=1;}void link(int x,int y){    makeroot(x);    pre[x]=y;}void cut(int x,int y){    makeroot(x);    expose(y);    splay(y);    pre[x]=ch[y][0]=0;}int find(int x){    expose(x);    splay(x);    while (ch[x][0]) x=ch[x][0];    return x;}int askdis(int x,int y){    makeroot(x);    expose(y);    splay(y);    return size[ch[y][0]];}ll asksum(int x,int y){    makeroot(x);    expose(y);    splay(y);    return sum[y]%mod;}void changepo(int x,ll z){    makeroot(x);    v[x]=z;    update(x);}void add(int x,int y,ll z){    makeroot(x);    expose(y);    splay(y);    cal(y,1,z);}void multi(int x,int y,ll z){    makeroot(x);    expose(y);    splay(y);    cal(y,z,0);}int main(){    scanf("%d%d",&n,&Q);    for (int i=1;i<=n;i++) {        ad[i]=0;        mul[i]=size[i]=v[i]=sum[i]=1;        rev[i]=0;    }    for (int i=1;i<n;i++)    {        int u,w;        scanf("%d%d",&u,&w);        link(u,w);    }    char s[10];    int u1,w1,u2,w2;    for (int i=1;i<=Q;i++)    {        scanf("%s",s);        if (s[0]=='+')        {            scanf("%d%d%d",&u1,&w1,&u2);            add(u1,w1,(ll)u2);         //改变路径权值         }        else if (s[0]=='-')        {            scanf("%d%d%d%d",&u1,&w1,&u2,&w2);            cut(u1,w1); link(u2,w2);        }        else if (s[0]=='*')        {            scanf("%d%d%d",&u1,&w1,&u2);            multi(u1,w1,(ll)u2);        }        else        {            scanf("%d%d",&u1,&w1);            printf("%d\n",asksum(u1,w1));        }    }    return 0;}
原创粉丝点击