SPOJ Query on a tree IV【LCT】

题目大意:

给定一个节点初始颜色全为白色的点,要求支持两种操作:
1.将节点x颜色翻转;
2.询问树中相距最远的两个白点的距离。
1n,m100000

解题思路:

为了叙述方便,其他的一些定义:ls左儿子,rs有儿子

首先把边权附到儿子结点上,记为len(len会引发很多细节问题,一定要小心)

实边上的信息维护一个lmax,rmax,maxs,sum。

lmax这个子splay表示的一段重链最浅的白点出发的最长链长,rmax从该实链最深的白点出发的最长链长(但不能越过fa[x]。这样lmax[rs],rmax[ls]就是实树中连接到x的链了),maxs即答案,包括实边虚边的最远两白点距离 ,sum表示该子splay段实链总长。

虚边就类似点分治时的想法,记录只到x点的链,和x子树中已经构成路径的两个白点(只管虚子树),用multiset维护。

先讨论x的虚子树对maxs[x]的影响,实边要复杂一些
最长和次长到x的链可以合并成一条路径
子树里已经形成的最大路径也可以更新
如果x是白点,那么maxs[x]就可以用最长链更新了

虚子树信息的维护主要是在access中的虚实切换

对于要从实变虚的原右儿子,maxs[rs]扔进路径的set里,lmax[rs]扔进链的set里,因为要连接到x点,所以是lmax
同样,从虚变实的新右儿子,把maxs[y]从set里删去,把lmax[y]从set里删去

虚部就完成了。

对于实链我们可以发现一个性质,LCT中splay的任意一个子树,对应的都是原树中实链连续的一段。

先更新lmax和rmax
lmax要过整棵子splay的最浅点
那么有lmax[x]=max(lmax[ls](不过x),max(虚链中最长+整个左儿子代表的一段,lmax[rs]+整个左儿子代表的一段))。
rmax同理,注意x对应的这条边,会有一些细节问题。

于是maxs就又有了两种新的更新方式
maxs[x]=max(maxs[x],rmax[ls]+max(虚链,lmax[rs]))
maxs[x]=max(maxs[x],lmax[rs]+max(虚链,rmax[ls]))

完结撒花。

#include<bits/stdc++.h>
#define ls son[x][0]
#define rs son[x][1]
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=100005,INF=0x3f3f3f3f;
int n,m,ans=-INF;
int tot,first[N],to[N<<1],w[N<<1],nxt[N<<1],c[N];
int son[N][2],fa[N],sum[N],lmax[N],rmax[N],maxs[N],val[N],len[N];
multiset<int>chain[N],path[N];
int fir(multiset<int> &s){return s.size()?*--s.end():-INF;}
int sec(multiset<int> &s){return s.size()>1?*--(--s.end()):-INF;}

void add(int x,int y,int z)
{
    nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}

void update(int x)
{
    sum[x]=sum[ls]+len[x]+sum[rs];
    int cha=max(val[x],fir(chain[x]));
    int L=max(cha,rmax[ls]+len[x]);
    int R=max(cha,lmax[rs]);
    lmax[x]=max(lmax[ls],sum[ls]+len[x]+R);
    rmax[x]=max(rmax[rs],sum[rs]+L);

    maxs[x]=max(rmax[ls]+len[x]+R,lmax[rs]+L);
    maxs[x]=max(maxs[x],max(maxs[ls],maxs[rs]));
    maxs[x]=max(maxs[x],fir(path[x]));
    maxs[x]=max(maxs[x],fir(chain[x])+sec(chain[x]));
    if(!val[x])maxs[x]=max(0,max(maxs[x],fir(chain[x])));
}

int which(int x){return son[fa[x]][1]==x;}

bool rt(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}

void rotate(int x)
{
    int y=fa[x],z=fa[y],t=which(x);
    if(!rt(y))son[z][which(y)]=x;
    fa[x]=z,fa[y]=x;
    son[y][t]=son[x][t^1],son[x][t^1]=y;
    if(son[y][t])fa[son[y][t]]=y;
    update(y),update(x);
}

void splay(int x)
{
    while(!rt(x))
    {
        if(!rt(fa[x]))
            which(x)==which(fa[x])?rotate(fa[x]):rotate(x);
        rotate(x);
    }
}

void access(int x)
{
    for(int y=0;x;y=x,x=fa[x])
    {
        splay(x);
        if(rs)chain[x].insert(lmax[rs]),path[x].insert(maxs[rs]);
        if(y)fa[y]=x,chain[x].erase(chain[x].find(lmax[y])),path[x].erase(path[x].find(maxs[y]));
        rs=y,update(x);
    }
}

void modify(int x)
{
    access(x),splay(x);
    c[x]^=1,val[x]=(!c[x])?0:-INF;
    update(x),ans=maxs[x];
}

void dfs(int u)
{
    for(int e=first[u];e;e=nxt[e])
    {
        int v=to[e];
        if(v==fa[u])continue;
        fa[v]=u,len[v]=w[e];
        dfs(v);
        chain[u].insert(lmax[v]),path[u].insert(maxs[v]);   
    }
    update(u);
}

int main()
{
    //freopen("lx.in","r",stdin);
    int x,y,z;
    n=getint();
    for(int i=1;i<n;i++)
    {
        x=getint(),y=getint(),z=getint();
        add(x,y,z),add(y,x,z);
    }
    for(int i=0;i<=n;i++)lmax[i]=rmax[i]=maxs[i]=-INF;
    dfs(1);ans=maxs[1];
    m=getint();char s[5];
    while(m--)
    {
        scanf("%s",s);
        if(s[0]=='A')
        {
            if(ans<0)puts("They have disappeared.");
            else printf("%d\n",ans);
        }
        else modify(getint());
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值