LCT总结

类比树剖,树剖是通过静态地把一棵树剖成若干条链然后用一种支持区间操作的数据结构维护(比如线段树、树状数组),而LCT是动态地去处理这个问题。

不少人都知道树剖用线段树维护,而LCT用\(splay\)维护。实际上同一条重链上的所有点才会被放在一棵\(splay\)中,而我们需要同时处理这若干条重链对应的若干棵\(splay\)之间的关系。因为一条重链上的每个点的深度互异,所以\(splay\)以深度\(dep\)为关键字。

我们规定一棵\(splay\)的根的\(fa\)为这条重链链顶节点在原树中的父亲节点。(前面的那个\(fa\)指的是\(splay\)中的\(fa\))
而显然认了这个父亲之后,父亲不会认这个儿子:很简单,这两个点不在同一条重链上,所以父亲的左右儿子一定没有它。由此可以写一个判断一个点是不是根节点(当前\(splay\)的根节点)的函数:

bool isroot(int x){
    return ls[fa[x]]!=x&&rs[fa[x]]!=x;
}

先讲LCT最重要的一个操作:\(access(x)\),表示把\(x\)节点到\(x\)所在树(连通块)的根节点之间的路径全部变成重路径。

void access(int x){
    for (int y=0;x;y=x,x=fa[x])
        splay(x),rs[x]=y,pushup(x);
}

相当于是说要把这些在路径上的点全部搞到一棵\(splay\)里面去。
从下往上做,每次把\(x\)splay到当前\(splay\)的根后,这个\(x\)就会处于一个很微妙的位置:它的右儿子\(rs\)应该会接上这条重链下面的一部分,因为这条重链下面一部分的深度显然会大于\(x\)。而所谓“下面的一部分”就是我们上一个处理的\(x\),这也就是为什么y=x,x=fa[x]了。
最开始的\(x\)的下面不应该接上任何东西,所以\(y\)的初值赋为0。

再就是另一个基本操作:\(makeroot(x)\),表示把\(x\)节点设为\(x\)所在树(连通块)的根节点。
我们先把\(x\)access了,即连接了\(x\)与根节点,然后再把\(x\)splay到根,那么显然这个\(x\)肯定会是\(splay\)中的最后一个元素(因为它深度最大),所以我们在\(x\)这里打一个\(rev\)标记(\(splay\)区间翻转操作)即可。

void makeroot(int x){
    access(x);splay(x);rev[x]^=1;
}

剩下的操作就很简单了。
\(findroot(x)\),找到\(x\)节点所在树(连通块)的根节点。用这个操作可以维护连通性。
\(findroot(x)=access(x)+splay(x)+\)找到最左边(深度最小)的那个节点。

int findroot(int x){
    access(x);splay(x);
    while (ls[x]) x=ls[x];
    return x;
}

\(split(x,y)\),抠出\(x\)到\(y\)的路径,抠完以后\(y\)是\(splay\)的根。
\(split(x,y)=makeroot(x)+access(y)+splay(y)\)

void split(int x,int y){
    makeroot(x);access(y);splay(y);
}

\(cut(x,y)\),砍断\(x\)到\(y\)的边。
\(cut(x,y)=split(x,y)+\)x的父亲和y的左儿子置0。
(split之后这一棵\(splay\)中就只剩\(x\)和\(y\)两个点了,所以才可以这么搞)

void cut(int x,int y){
    split(x,y);fa[x]=ls[y]=0;
}

\(link(x,y)\),连接\(x\)到\(y\)的边。
\(cut(x,y)=makeroot(x)+fa[x]=y\)

void link(int x,int y){
    makeroot(x);fa[x]=y;
}

【模板】LCT

路径异或和+动态连边删边+单点修改。

#include<cstdio>
#include<algorithm>
using namespace std;
const int _ = 300005;
int n,m,opt,a,b,fa[_],ls[_],rs[_],sum[_],val[_],rev[_],Stack[_],top;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
void pushup(int x){sum[x]=sum[ls[x]]^sum[rs[x]]^val[x];}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
    int y=fa[x],z=fa[y];
    ls[y]=rs[x];
    if (rs[x]) fa[rs[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    rs[x]=y;fa[y]=x;
    pushup(y);
}
void L_rotate(int x)
{
    int y=fa[x],z=fa[y];
    rs[y]=ls[x];
    if (ls[x]) fa[ls[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    ls[x]=y;fa[y]=x;
    pushup(y);
}
void splay(int x)
{
    Stack[++top]=x;
    for (int i=x;!isroot(i);i=fa[i])
        Stack[++top]=fa[i];
    while (top) pushdown(Stack[top--]);
    while (!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if (isroot(y))
            if (x==ls[y]) R_rotate(x);
            else L_rotate(x);
        else
            if (y==ls[z])
                if (x==ls[y]) R_rotate(y),R_rotate(x);
                else L_rotate(x),R_rotate(x);
            else
                if (x==ls[y]) R_rotate(x),L_rotate(x);
                else L_rotate(y),L_rotate(x);
    }
    pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);fa[x]=ls[y]=0;}
void link(int x,int y){makeroot(x);fa[x]=y;}
int main()
{
    n=gi();m=gi();
    for (int i=1;i<=n;i++) val[i]=gi();
    while (m--)
    {
        opt=gi();a=gi();b=gi();
        if (opt==0)
            split(a,b),printf("%d\n",sum[b]);
        if (opt==1)
            if (findroot(a)!=findroot(b))
                link(a,b);
        if (opt==2)
            if (findroot(a)==findroot(b))
                cut(a,b);
        if (opt==3)
            access(a),splay(a),val[a]=b,pushup(a);
    }
    return 0;
}

[SDOI2008]Cave 洞穴勘测

LCT维护连通性。

#include<cstdio>
#include<algorithm>
using namespace std;
const int _ = 10005;
int n,m,a,b,fa[_],ls[_],rs[_],rev[_],Stack[_],top;
char s[_];
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
    int y=fa[x],z=fa[y];
    ls[y]=rs[x];
    if (rs[x]) fa[rs[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    rs[x]=y;fa[y]=x;
}
void L_rotate(int x)
{
    int y=fa[x],z=fa[y];
    rs[y]=ls[x];
    if (ls[x]) fa[ls[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    ls[x]=y;fa[y]=x;
}
void splay(int x)
{
    Stack[++top]=x;
    for (int i=x;!isroot(i);i=fa[i])
        Stack[++top]=fa[i];
    while (top) pushdown(Stack[top--]);
    while (!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if (isroot(y))
            if (x==ls[y]) R_rotate(x);
            else L_rotate(x);
        else
            if (y==ls[z])
                if (x==ls[y]) R_rotate(y),R_rotate(x);
                else L_rotate(x),R_rotate(x);
            else
                if (x==ls[y]) R_rotate(x),L_rotate(x);
                else L_rotate(y),L_rotate(x);
    }
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y;}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);ls[y]=fa[x]=0;}
void link(int x,int y){makeroot(x),fa[x]=y;}
int main()
{
    n=gi();m=gi();
    while (m--)
    {
        scanf("%s",s);a=gi();b=gi();
        if (s[0]=='Q')
            puts(findroot(a)==findroot(b)?"Yes":"No");
        if (s[0]=='C')
            link(a,b);
        if (s[0]=='D')
            cut(a,b);
    }
    return 0;
}

[HNOI2010]BOUNCE 弹飞绵羊

LCT动态维护深度。

其实就是求到根节点路径上的size-1。
对于每一个会弹飞的点,连到建立的超级根上去。这样就保证始终是一棵树。(主要是为了方便\(split\))

#include<cstdio>
#include<algorithm>
using namespace std;
const int _ = 200005;
int n,m,opt,pos,fa[_],ls[_],rs[_],sz[_],nxt[_],rev[_],Stack[_],top;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
void pushup(int x){sz[x]=sz[ls[x]]+sz[rs[x]]+1;}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
    int y=fa[x],z=fa[y];
    ls[y]=rs[x];
    if (rs[x]) fa[rs[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    rs[x]=y;fa[y]=x;
    pushup(y);
}
void L_rotate(int x)
{
    int y=fa[x],z=fa[y];
    rs[y]=ls[x];
    if (ls[x]) fa[ls[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    ls[x]=y;fa[y]=x;
    pushup(y);
}
void splay(int x)
{
    Stack[++top]=x;
    for (int i=x;!isroot(i);i=fa[i])
        Stack[++top]=fa[i];
    while (top) pushdown(Stack[top--]);
    while (!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if (isroot(y))
            if (x==ls[y]) R_rotate(x);
            else L_rotate(x);
        else
            if (y==ls[z])
                if (x==ls[y]) R_rotate(y),R_rotate(x);
                else L_rotate(x),R_rotate(x);
            else
                if (x==ls[y]) R_rotate(x),L_rotate(x);
                else L_rotate(y),L_rotate(x);
    }
    pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);ls[y]=fa[x]=0;}
void link(int x,int y){makeroot(x);fa[x]=y;}
int main()
{
    n=gi();
    for (int i=1;i<=n;i++)
    {
        nxt[i]=min(i+gi(),n+1);
        link(i,nxt[i]);
    }
    m=gi();
    while (m--)
    {
        opt=gi();pos=gi()+1;
        if (opt==1)
            split(n+1,pos),printf("%d\n",sz[pos]-1);
        else
        {
            cut(pos,nxt[pos]);
            nxt[pos]=min(pos+gi(),n+1);
            link(pos,nxt[pos]);
        }
    }
    return 0;
}

[NOI2014]魔法森林

LCT维护最小生成树。

先把所有边按a值排序,然后维护以b为权值的最小生成树。
LCT处理边权信息:当做一条边是一个点,link就是连两次。
维护最小生成树:维护一段路径上的边权最大值和最大值的位置,新连一条边的时候讨论:
1、若要连接的两个点已经连通,那么判断边权最大值是否比要连的边的边权大。如果要大就把最大的那个位置断掉,新加入的边连上。若不大于就continue
2、若要连接的两个点未联通,那么直接连没话说。
然后这题差不多就做完了。

#include<cstdio>
#include<algorithm>
using namespace std;
#define inf 1e9
const int _ = 200005;
struct edge
{
    int u,v,a,b;
    bool operator < (const edge &zsy) const{return a==zsy.a?b<zsy.b:a<zsy.a;}
}e[_];
int n,m,fa[_],ls[_],rs[_],rev[_],val[_],mx[_],Stack[_],top,ans=inf;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
void pushup(int x)
{
    mx[x]=x;
    mx[x]=val[mx[ls[x]]]>val[mx[x]]?mx[ls[x]]:mx[x];
    mx[x]=val[mx[rs[x]]]>val[mx[x]]?mx[rs[x]]:mx[x];
}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
    int y=fa[x],z=fa[y];
    ls[y]=rs[x];
    if (rs[x]) fa[rs[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    rs[x]=y;fa[y]=x;
    pushup(y);
}
void L_rotate(int x)
{
    int y=fa[x],z=fa[y];
    rs[y]=ls[x];
    if (ls[x]) fa[ls[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    ls[x]=y;fa[y]=x;
    pushup(y);
}
void splay(int x)
{
    Stack[++top]=x;
    for (int i=x;!isroot(i);i=fa[i])
        Stack[++top]=fa[i];
    while (top) pushdown(Stack[top--]);
    while (!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if (isroot(y))
            if (x==ls[y]) R_rotate(x);
            else L_rotate(x);
        else
            if (y==ls[z])
                if (x==ls[y]) R_rotate(y),R_rotate(x);
                else L_rotate(x),R_rotate(x);
            else
                if (x==ls[y]) R_rotate(x),L_rotate(x);
                else L_rotate(y),L_rotate(x);
    }
    pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);fa[x]=ls[y]=0;}
void link(int x,int y){makeroot(x);fa[x]=y;}
int main()
{
    n=gi();m=gi();
    for (int i=n+1;i<=n+m;i++)
        e[i].u=gi(),e[i].v=gi(),e[i].a=gi(),e[i].b=gi();
    sort(e+n+1,e+n+m+1);
    for (int i=n+1,u,v,a,b,pos;i<=n+m;i++)
    {
        u=e[i].u;v=e[i].v;a=e[i].a;b=e[i].b;
        val[i]=b;mx[i]=i;
        if (findroot(u)==findroot(v))
        {
            split(u,v);pos=mx[v];
            if (val[pos]>b)
                cut(pos,e[pos].u),cut(pos,e[pos].v);
            else continue;
        }
        link(i,u);link(i,v);
        if (findroot(1)==findroot(n))
            split(1,n),ans=min(ans,a+val[mx[n]]);
    }
    printf("%d\n",ans==inf?-1:ans);
    return 0;
}

[ZJOI2012]网络

很显然对于每个颜色维护一个LCT。
对于操作1:边直接暴力找(每个点连的边不超过20条),先判No such edge.,然后判一下同色修改(等于没修改)直接Success.。如果两个点新颜色的边有一个等于2就Error 1.,再是若已经连通Error 2.,最后修改并Success.

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
const int _ = 10005;
struct edge{int to,next,col;}a[_*20];
int N,M,C,K,val[_],fa[10][_],ls[10][_],rs[10][_],s[10][_],tag[10][_],Stack[_],top,cnt,head[_];
struct node{int x,y;};
map<node,int>col;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
void pushup(int x,int c){s[c][x]=max(max(s[c][ls[c][x]],s[c][rs[c][x]]),val[x]);}
bool isroot(int x,int c){return ls[c][fa[c][x]]!=x&&rs[c][fa[c][x]]!=x;}
void pushdown(int x,int c){if (!tag[c][x]) return;swap(ls[c][x],rs[c][x]);tag[c][ls[c][x]]^=1;tag[c][rs[c][x]]^=1;tag[c][x]=0;}
void R_rotate(int x,int c)
{
    int y=fa[c][x],z=fa[c][y];
    ls[c][y]=rs[c][x];
    if (rs[c][x]) fa[c][rs[c][x]]=y;
    fa[c][x]=z;
    if (!isroot(y,c)) if (y==ls[c][z]) ls[c][z]=x;else rs[c][z]=x;
    rs[c][x]=y;fa[c][y]=x;
    pushup(y,c);
}
void L_rotate(int x,int c)
{
    int y=fa[c][x],z=fa[c][y];
    rs[c][y]=ls[c][x];
    if (ls[c][x]) fa[c][ls[c][x]]=y;
    fa[c][x]=z;
    if (!isroot(y,c)) if (y==ls[c][z]) ls[c][z]=x;else rs[c][z]=x;
    ls[c][x]=y;fa[c][y]=x;
    pushup(y,c);
}
void splay(int x,int c)
{
    Stack[++top]=x;
    for (int i=x;!isroot(i,c);i=fa[c][i])
        Stack[++top]=fa[c][i];
    while (top) pushdown(Stack[top--],c);
    while (!isroot(x,c))
    {
        int y=fa[c][x],z=fa[c][y];
        if (isroot(y,c))
            if (x==ls[c][y]) R_rotate(x,c);
            else L_rotate(x,c);
        else
            if (y==ls[c][z])
                if (x==ls[c][y]) R_rotate(y,c),R_rotate(x,c);
                else L_rotate(x,c),R_rotate(x,c);
            else
                if (x==ls[c][y]) R_rotate(x,c),L_rotate(x,c);
                else L_rotate(y,c),L_rotate(x,c);
    }
    pushup(x,c);
}
void access(int x,int c){for (int y=0;x;y=x,x=fa[c][x]) splay(x,c),rs[c][x]=y,pushup(x,c);}
void makeroot(int x,int c){access(x,c);splay(x,c);tag[c][x]^=1;}
int findroot(int x,int c){access(x,c);splay(x,c);while (ls[c][x]) x=ls[c][x];return x;}
void split(int x,int y,int c){makeroot(x,c);access(y,c);splay(y,c);}
void cut(int x,int y,int c){split(x,y,c);ls[c][y]=fa[c][x]=0;}
void link(int x,int y,int c){makeroot(x,c);fa[c][x]=y;}
int main()
{
    N=gi();M=gi();C=gi();K=gi();
    for (int i=1;i<=N;i++)
        val[i]=gi();
    for (int i=1,u,v,w;i<=M;i++)
    {
        u=gi();v=gi();w=gi();
        link(u,v,w);
        a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
        a[++cnt]=(edge){u,head[v],w};head[v]=cnt;
    }
    for (int i=1,opt,u,v,w,e1,e2,t1,t2;i<=K;i++)
    {
        opt=gi();
        if (opt==0)
        {
            u=gi();v=gi();val[u]=v;
            for (w=0;w<C;w++)
                makeroot(u,w),pushup(u,w);
        }
        if (opt==1)
        {
            u=gi();v=gi();w=gi();
            for (e1=head[u];e1;e1=a[e1].next)
                if (a[e1].to==v) break;
            for (e2=head[v];e2;e2=a[e2].next)
                if (a[e2].to==u) break;
            if (!e1) {puts("No such edge.");continue;}
            if (a[e1].col==w) {puts("Success.");continue;}
            t1=t2=0;
            for (int e=head[u];e;e=a[e].next)
                if (a[e].col==w) t1++;
            for (int e=head[v];e;e=a[e].next)
                if (a[e].col==w) t2++;
            if (t1==2||t2==2) {puts("Error 1.");continue;}
            if (findroot(u,w)==findroot(v,w)) {puts("Error 2.");continue;}
            cut(u,v,a[e1].col);a[e1].col=a[e2].col=w;link(u,v,w);puts("Success.");
        }
        if (opt==2)
        {
            w=gi();u=gi();v=gi();
            if (findroot(u,w)!=findroot(v,w)) puts("-1");
            else split(u,v,w),printf("%d\n",s[w][v]);
        }
    }
    return 0;
}

[WC2007]水管局长

一张n点m边无向图,支持两种操作:1、查询x到y的路径中边权最大值最小。2、删除边(x,y),保证边存在。

LCT维护最小生成树

删边操作在把所用询问输入之后倒序处理变成加边操作
然后注意在将边和询问排序的时候的顺序(真的是好烦啊)

#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1100005;
struct edge{int u,v,w,b,id;}e[N];
struct query{int opt,u,v,x,id;}q[N];
int n,m,Q,fa[N],ls[N],rs[N],rev[N],mx[N],val[N],Stack[N],top;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void pushup(int x)//维护最大值的位置
{
    mx[x]=x;
    if (val[mx[x]]<val[mx[ls[x]]]) mx[x]=mx[ls[x]];
    if (val[mx[x]]<val[mx[rs[x]]]) mx[x]=mx[rs[x]];
}
void R_rotate(int x)
{
    int y=fa[x],z=fa[y];
    ls[y]=rs[x];
    if (rs[x]) fa[rs[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    rs[x]=y;fa[y]=x;
    pushup(y);
}
void L_rotate(int x)
{
    int y=fa[x],z=fa[y];
    rs[y]=ls[x];
    if (ls[x]) fa[ls[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    ls[x]=y;fa[y]=x;
    pushup(y);
}
void splay(int x)
{
    Stack[++top]=x;
    for (int i=x;!isroot(i);i=fa[i])
        Stack[++top]=fa[i];
    while (top) pushdown(Stack[top--]);
    while (!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if (isroot(y))
            if (x==ls[y]) R_rotate(x);
            else L_rotate(x);
        else
            if (y==ls[z])
                if (x==ls[y]) R_rotate(y),R_rotate(x);
                else L_rotate(x),R_rotate(x);
            else
                if (x==ls[y]) R_rotate(x),L_rotate(x);
                else L_rotate(y),L_rotate(x);
    }
    pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);fa[x]=ls[y]=0;}
void link(int x,int y){makeroot(x);fa[x]=y;}

bool cmp1(edge a,edge b){return a.u==b.u?a.v<b.v:a.u<b.u;}//边按u第一关键字v第二关键字排序
bool cmp2(query a,query b){return a.u==b.u?a.v<b.v:a.u<b.u;}//询问按u第一关键字v第二关键字排序
bool cmp3(edge a,edge b){return a.w<b.w;}//边按边权排序
bool cmp4(query a,query b){return a.id<b.id;}//询问按顺序
bool cmp5(edge a,edge b){return a.id<b.id;}//边按顺序
int main()
{
    n=gi();m=gi();Q=gi();
    for (int i=1;i<=m;i++)
    {
        e[i]=(edge){gi(),gi(),gi(),0,i};
        if (e[i].u>e[i].v) swap(e[i].u,e[i].v);
        val[i+n]=e[i].w;mx[i+n]=i+n;
    }
    sort(e+1,e+m+1,cmp1);
    for (int i=1;i<=Q;i++)
    {
        q[i]=(query){gi(),gi(),gi(),0,i};
        if (q[i].u>q[i].v) swap(q[i].u,q[i].v);
    }
    sort(q+1,q+Q+1,cmp2);
    int pos=1;
    for (int i=1;i<=Q;i++)
        if (q[i].opt==2)
        {
            while (e[pos].u!=q[i].u||e[pos].v!=q[i].v) pos++;
            q[i].x=e[pos].id;e[pos].b=1;
        }
    sort(e+1,e+m+1,cmp3);
    int cnt=0;
    for (int i=1;i<=m;i++)
        if (e[i].b==0&&findroot(e[i].u)!=findroot(e[i].v))
        {
            cnt++;
            link(e[i].u,e[i].id+n),link(e[i].v,e[i].id+n);//注意这里link的是e[i].id而不是i!!!
            if (cnt==n-1) break;
        }
    sort(q+1,q+Q+1,cmp4);
    sort(e+1,e+m+1,cmp5);
    for (int i=Q,pos;i;i--)
    {
        split(q[i].u,q[i].v);pos=mx[q[i].v];
        if (q[i].opt==1)
            q[i].x=val[pos];
        else if (e[q[i].x].w<val[pos])
        {
            cut(pos,e[pos-n].u);cut(pos,e[pos-n].v);
            link(q[i].x+n,q[i].u);link(q[i].x+n,q[i].v);
        }
    }
    for (int i=1;i<=Q;i++)
        if (q[i].opt==1)
            printf("%d\n",q[i].x);
    return 0;
}

【THUWC 2017】在美妙的数学王国中畅游

数学王国里有n座城市,每座城市有三个参数\(f\),\(a\),\(b\),一个智商为\(x\)的人经过一座城市的获益\(f(x)\)是
若\(f=1\),则\(f(x)=sin(ax+b)\);
若\(f=2\),则\(f(x)=e^{ax+b}\);
若\(f=3\),则\(f(x)=ax+b\);
会发生如下四种事件:
1、有两个城市之间新建了道路;
2、有两个城市之间的道路被摧毁了;
3、城市i的三个参数被修改了;
4、求智商为x的人从u走到v的获益总和。
保证任何时候图是一个森林。

这是一道数学题

公式太难打了QWQ
总之就是维护出泰勒展开中每一项的\(\sum\)系数。精度的话算到12、13位就行了。
然而这份代码交到BZOJ上还是T

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 100005;
const int M = 13;
const double e = 2.718281828;
int n,m,fa[N],ls[N],rs[N],rev[N],f[N],Stack[N],top;
double jc[M],sum[M][N],a[N],b[N];
char type[N],s[N];
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushup(int x)
{
    for (int i=0;i<M;i++)
        sum[i][x]=sum[i][ls[x]]+sum[i][rs[x]];
    if (f[x]==1)
    {
        double val=1;
        for (int i=0;i<M;i++)
        {
            if (i%4==0) sum[i][x]+=sin(b[x])*val;
            if (i%4==1) sum[i][x]+=cos(b[x])*val;
            if (i%4==2) sum[i][x]+=-sin(b[x])*val;
            if (i%4==3) sum[i][x]+=-cos(b[x])*val;
            val*=a[x];
        }
    }
    if (f[x]==2)
    {
        double val=pow(e,b[x]);sum[0][x]+=val;
        for (int i=1;i<M;i++)
            val*=a[x],sum[i][x]+=val;
    }
    if (f[x]==3)
        sum[0][x]+=b[x],sum[1][x]+=a[x];
}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
    int y=fa[x],z=fa[y];
    ls[y]=rs[x];
    if (rs[x]) fa[rs[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    rs[x]=y;fa[y]=x;
    pushup(y);
}
void L_rotate(int x)
{
    int y=fa[x],z=fa[y];
    rs[y]=ls[x];
    if (ls[x]) fa[ls[x]]=y;
    fa[x]=z;
    if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
    ls[x]=y;fa[y]=x;
    pushup(y);
}
void splay(int x)
{
    Stack[++top]=x;
    for (int i=x;!isroot(i);i=fa[i])
        Stack[++top]=fa[i];
    while (top) pushdown(Stack[top--]);
    while (!isroot(x))
    {
        int y=fa[x],z=fa[y];
        if (isroot(y))
            if (x==ls[y]) R_rotate(x);
            else L_rotate(x);
        else
            if (y==ls[z])
                if (x==ls[y]) R_rotate(y),R_rotate(x);
                else L_rotate(x),R_rotate(x);
            else
                if (x==ls[y]) R_rotate(x),L_rotate(x);
                else L_rotate(y),L_rotate(x);
    }
    pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void link(int x,int y){makeroot(x);fa[x]=y;}
void cut(int x,int y){split(x,y);ls[y]=fa[x]=0;}
int main()
{
    freopen("math.in","r",stdin);
    freopen("math.out","w",stdout);
    jc[0]=1;
    for (int i=1;i<M;i++)
        jc[i]=jc[i-1]*i;
    scanf("%d %d %s",&n,&m,type);
    for (int i=1;i<=n;i++)
        scanf("%d %lf %lf",&f[i],&a[i],&b[i]);
    while (m--)
    {
        int u,v,ff;
        double aa,bb,x,IQ,ans;
        scanf("%s",s);
        if (s[0]=='a')
        {
            scanf("%d %d",&u,&v);
            u++;v++;
            link(u,v);
        }
        if (s[0]=='d')
        {
            scanf("%d %d",&u,&v);
            u++;v++;
            cut(u,v);
        }
        if (s[0]=='m')
        {
            scanf("%d %d %lf %lf",&u,&ff,&aa,&bb);
            u++;
            makeroot(u);
            f[u]=ff;a[u]=aa;b[u]=bb;
            pushup(u);
        }
        if (s[0]=='t')
        {
            scanf("%d %d %lf",&u,&v,&IQ);x=1;
            u++;v++;
            if (findroot(u)^findroot(v)) {puts("unreachable");continue;}
            split(u,v);
            ans=0;
            for (int i=0;i<M;i++)
                ans+=sum[i][v]*x/jc[i],x*=IQ;
            printf("%.8e\n",ans);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zhoushuyu/p/8137553.html

时间: 2024-10-12 01:22:59

LCT总结的相关文章

luoguP2590 [ZJOI2008]树的统计 [树链剖分] [TLE的LCT]

题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 输入输出格式 输入格式: 输入文件的第一行为一个整数n,表示节点的个数. 接下来n – 1行,每行2个整数a和b,表示节点a和节点b之

[BZOJ2049] [CodeVS1839] [SDOI2008] Cave 洞穴勘测 (LCT)

Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好两个洞穴.假如两个洞穴可以通过一条或者多条通道按一定顺序连接起来,那么这两个洞穴就是连通的,按顺序连接在一起的这些通道则被称之为这两个洞穴之间的一条路径.洞穴都十分坚固无法破坏,然而通道不太稳定,时常因为外界影响而发生改变,比如,根据有关仪器的监测结果,123号洞穴和127号洞穴之间有时会出现一条通

一些LCT裸题

又来回炉lct了= = [bzoj3514]: Codechef MARCH14 GERALD07加强版 模版题.常见姿势,把边也当成点. 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int maxn=200233<<1; 6 const int inf=1000023333; 7 struct zs{ 8 int u,v

[BZOJ2002][Hnoi2010]Bounce弹飞绵羊 LCT

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2002 建图,每次往后面跳就往目标位置连边,将跳出界的点设为同一个点.对于修改操作发现可以用LCT维护图的连通性,然后用size域维护跳的点的次数就行了. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 int inline readint(

luoguP3690 【模板】Link Cut Tree (动态树)[LCT]

题目背景 动态树 题目描述 给定N个点以及每个点的权值,要你处理接下来的M个操作.操作有4种.操作从0到3编号.点从1到N编号. 0:后接两个整数(x,y),代表询问从x到y的路径上的点的权值的xor和.保证x到y是联通的. 1:后接两个整数(x,y),代表连接x到y,若x到Y已经联通则无需连接. 2:后接两个整数(x,y),代表删除边(x,y),不保证边(x,y)存在. 3:后接两个整数(x,y),代表将点X上的权值变成Y. 输入输出格式 输入格式: 第1行两个整数,分别为N和M,代表点数和操

HDOJ 4010 Query on The Trees LCT

LCT: 分割.合并子树,路径上全部点的点权添加一个值,查询路径上点权的最大值 Query on The Trees Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others) Total Submission(s): 2582    Accepted Submission(s): 1208 Problem Description We have met so many problems

【BZOJ3514】Codechef MARCH14 GERALD07加强版(LCT)

题意:N个点M条边的无向图,q次询问保留图中编号在[l,r]的边的时候图中的联通块个数. 询问加密,强制在线 n,m,q<=200000 题意:RYZ作业 以下转载自hzwer http://hzwer.com/4358.html 本人实力有限难以清晰描述 有一个比较猎奇的做法:首先把边依次加到图中,若当前这条边与图中的边形成了环,那么把这个环中最早加进来的边弹出去并将每条边把哪条边弹了出去记录下来:ntr[i] = j,特别地,要是没有弹出边,ntr[i] = 0;这个显然是可以用LCT来弄的

BZOJ_3282_Tree_(LCT)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3282 给出n个点以及权值,四种操作: 0.求x,y路径上的点权值的异或和. 1.连接x,y. 2.断开x,y. 3.将x的权值改为t. 分析 LCT模板题. 说几点自己的感悟和需要注意的地方吧(这里把原来的树称作树,平衡树称作Splay以避免混淆): 1.LCT是用Splay来维护一个森林(通过将链剖分),Splay以深度为关键字,即深度小的在左边,深度大的在右边.森林中每一棵树都有一个或多

BZOJ_2002_弹飞绵羊_(LCT)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2002 一列n个数,a[i]表示向后a[i]个,问第k个数进行多少次向后跳跃会飞出去. 分析 i连向i+a[i],那么我们建立一个森林,i是i+a[i]的一个子节点,如果i+a[i]>n,那么i连向null.这样对于节点k,问多少次飞出去,就是向上走多少个到null,也就是深度是多少,直接LCt处理. 注意: 1.这里的link并不以LCT中普遍的link.普通的link是将两个不想连的点连在

BZOJ 2002: [Hnoi2010]Bounce 弹飞绵羊( LCT )

LCT... ---------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define rep( i , n ) for( int i = 0 ; i < n ; ++i ) #define clr( x , c ) memset( x