bzoj1180 [CROATIAN2009]OTOCI(详解LCT操作)

来源:互联网 发布:大连爱知时计科技 编辑:程序博客网 时间:2024/05/18 21:39

Description

给出n个结点以及每个点初始时对应的权值wi。起始时点与点之间没有连边。有3类操作: 1、bridge A B:询问结点A与结点B是否连通。如果是则输出“no”。否则输出“yes”,并且在结点A和结点B之间连一条无向边。 2、penguins A X:将结点A对应的权值wA修改为X。 3、excursion A B:如果结点A和结点B不连通,则输出“impossible”。否则输出结点A到结点B的路径上的点对应的权值的和。给出q个操作,要求在线处理所有操作。数据范围:1<=n<=30000, 1<=q<=300000, 0<=wi<=1000。

Input

第一行包含一个整数n(1<=n<=30000),表示节点的数目。第二行包含n个整数,第i个整数表示第i个节点初始时对应的权值。第三行包含一个整数q(1<=n<=300000),表示操作的数目。以下q行,每行包含一个操作,操作的类别见题目描述。任意时刻每个节点对应的权值都是1到1000的整数。

Output

输出所有bridge操作和excursion操作对应的输出,每个一行。

Sample Input

5
4 2 4 5 6
10
excursion 1 1
excursion 1 2
bridge 1 2
excursion 1 2
bridge 3 4
bridge 3 5
excursion 4 5
bridge 1 3
excursion 2 4
excursion 2 5

Sample Output

4
impossible
yes
6
yes
yes
15
yes
15
16


分析:发现一个讲得很好的[blog](http://blog.csdn.net/JeremyGJY/article/details/51078087)这篇blog使我对LCT的各个操作有了更清晰的理解我们只要记住:

expose是访问操作,可以把x与根节点的路径变成偏爱路径

makeroot是换根操作,可以把树形结构的根换成x

其余的所有操作都是建立在这两个操作上的

Cut:

我们需要断开(x,y)
同理,我们先把x变成整棵树的根节点
之后我们expose(y),splay(y),
因为(x,y)之前是相连的,所以这样操作之后,x和y一定在一个辅助树中
x是根节点,ta的深度最小,y是辅助树的根节点
所以x一定是y的左儿子
pre[x]=0,ch[y][0]=0

我们要连接(x,y)
既然我们能够cut,那我们就可以link
于是我们可以先把整棵树的根节点变成x:(makeroot(x)),这样x就没有pre
所以我们只要简单的把pre[x]变成y即可

Find:

这个操作可以把找到当前树结构中的根节点
我们只要访问一下x,把ta转换到辅助树的根上
之后一路向左找就可以了(根节点一定是深度最浅的一个点)

两点连通性:

find(x)==find(y)
显而易见

路径权值和:

LCT的优点就是ta的形态不固定
我们可以把路径中的一个端点视为根节点(比如说x):makeroot(x)
之后只要expose(y),这样x到y的路径就变成了偏爱路径,
splay(y),y节点上的sum就是路径权值和啦

节点到根的距离:

首先我们要把给定的根换到根的位置上:makeroot(root)
之后就像询问路径权值和一样
expose(x),splay(x),
返回值是size[ch[x][0]]

更改节点值:

我们要改变一个结点的权值,
我们当然希望涉及到的结点尽量少,那么什么样的结点改变ta的值影响的结点维护值最少呢,当然是根节点
所以我们首先makeroot(x)
直接修改x的值就可以了
最后不要忘了update(x)

tip

rotate,splay,expose,makeroot这四个过程是最重要的
一定不要写错了

#include<cstdio>#include<cstring>#include<iostream>using namespace std;const int N=300010;int pre[N],ch[N][2],v[N],sum[N],q[N];bool rev[N];int n,m;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]];}void push(int bh){    int lc=ch[bh][0];    int rc=ch[bh][1];    if (bh&&rev[bh])    {        if (lc) rev[lc]^=1;        if (rc) rev[rc]^=1;        rev[bh]^=1;        swap(ch[bh][0],ch[bh][1]);    }}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])      //!isroot(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);      //产生了一个新儿子,所以需要update         t=bh;        bh=pre[bh];    }}void makeroot(int bh)    //把bh变成树的根 {    expose(bh);    splay(bh);    rev[bh]^=1;}void link(int x,int y){    makeroot(x);         //这样x就没有pre了     pre[x]=y;     }void cut(int x,int y){    makeroot(x);    expose(y); splay(y); //这样x一定在y的左儿子中     pre[x]=ch[y][0]=0;    update(x);}int find(int bh){    expose(bh);          //访问一下,使得根到bh的路径变成偏爱路径     splay(bh);    while (ch[bh][0]) bh=ch[bh][0];    return bh;}int main(){    char s[30];    scanf("%d",&n);    for (int i=1;i<=n;i++) scanf("%d",&v[i]),sum[i]=v[i];    scanf("%d",&m);    for (int i=1;i<=m;i++)    {        scanf("%s",s);        int x,y;        scanf("%d%d",&x,&y);        if (s[0]=='e')          //路径权值和         {            if (find(x)!=find(y)) printf("impossible\n");            else            {                makeroot(x);                expose(y);                splay(y);                printf("%d\n",sum[y]);            }        }        else if (s[0]=='b')        {            if (find(x)!=find(y))                printf("yes\n"),link(x,y);            else printf("no\n");        }        else        {            makeroot(x);       //改变单个结点值             splay(x);            v[x]=y;            update(x);        }    }    return 0;}
原创粉丝点击