GMOJ 3883. 【NOIP2014模拟】线段树(segment) 题解

GMOJ 3883. 【NOIP2014模拟】线段树(segment) 题解

Warning:本做法非一般做法。

首先

  1. 把边权下放成点权(即每个点的颜色表示其到父亲结点的边的颜色),以下内容都是按点权写的。

  2. 询问的两点称AB,修改时的当前点称X A与B的LCA称LCA

  3. \(UP\)数组为当前点上面有可能能修改的最深的节点(默认为该点的父亲)可以理解为树链剖分的top

    一堆废话

简略版

程序分五步

  1. 离线倒序处理。
  2. 然后,求每对询问点的LCA。

  3. 然后,把AB的最短路径拆成A到LCA和B到LCA的两条路径。

  4. 然后,暴力跳边修改(不修改LCA)。

  5. 输出答案。

结束了,完结撒花*\( ̄▽ ̄)/*。

非常好我啥也没讲(逃)。

详细版

  1. 好理解,不解释。
  2. 使用Tarjan算法求LCA。什么?你不知道求LCA的Tarjan算法?戳我学习
  3. 不解释。
  4. 你看,两条路径的重叠部分的顶端一定是这两条路径的LCA的深的那个,对不对 不对,没证明没真相 。所以我们把\(UP_X\)设为当前这条路径的顶端(LCA)。因为LCA我们不能修改,所以上面有可能能修改的最深的节点(\(UP\)的定义)为LCA。
  5. ……

FAQ

  1. 我的Tarjan超时了:不可能,那是你的问题
  2. 如果\(UP\)设为LCA后LCA也被修改了呢?:那是修改LCA那条路径要干的事,跟我有什么关系,跳了修改不了就再跳。
  3. 怎么跳边?:一直将当前点设为\(UP_x\)
  4. 我的Tarjan爆栈了:这正是我接下来要讲的:

模拟栈版Tarjan

Hint:该思想可用于所有的可以确定栈的大小的递归(说白了就是dfs)。

一般的Tarjan这道题是只能拿80分的,因为这题卡栈。

系统分配的栈的太小了,我们可以通过模拟自己的栈来规避这个限制。

实在不好讲,直接上模板。

代码大致模板:

while(栈未空){
    if(第一次进栈){  //遍历变量为0
        处理信息     //结点进入时可以自己处理的信息
        设置遍历变量
    }else{
        处理信息     //结点退出后需要其父亲处理的信息
        更改遍历变量
    }
    if(可以遍历该结点||遍历变量为0){
        if(可以遍历该结点){
            栈大小++
            栈顶=该结点
            处理信息  //结点进入时需要其父亲处理的信息
        }else{
            栈大小--
            处理信息  //结点退出后可以自己处理的信息
        }
    }
}

上面的代码有兴趣的可以背下来,不过其实没什么用。我能想到的就只有放卡栈的题目多骗几分

模拟栈版Tarjan核心代码:

//size为栈的大小
//st为栈
//i[x]为栈的第x项的遍历变量
//uni为Tarjan的并查集的合并
//root为并查集的……你懂的
//dp为深度
//b[x][0]下一条边 b[x][1]入点
//a为边的头
//fh为询问的头
//f[x][0]下一个询问 f[x][1]询问的另一点 f[x][2]询问的编号
//lca顾名思义
while(size){
    if(!i[size]){
        i[size]=a[st[size]];   //设置遍历变量
    }else{
        uni(b[i[size]][1],st[size]);  //结点退出后需要其父亲处理的信息
        i[size]=b[i[size]][0];  //更改遍历变量
    }
    if(i[size]){
        dp[b[i[size]][1]]=dp[st[size]]+1; //结点进入时需要其父亲处理的信息
        st[size+1]=b[i[size]][1];  //设置栈顶
        size++;  //增加栈的大小
    }else{
        for(int i=fh[st[size]];i;i=f[i][0]){   //Tarjan核心代码
            int get=root(f[i][1]);
            if((get!=f[i][1])||(get==st[size])){
                lca[f[i][2]]=get;
            }
        }
        size--;  //退栈
    }
}

真·完结撒花*\( ̄▽ ̄)/*

完整代码

压行编译开关:

1. 滥用for循环,=
2. 允许?运算符
3. 相似数据赋初值压行
#include<cstdio>
#define rg register
#define N 500010
using namespace std;
int n,m,s[N],a[N],fh[N],b[N][2],f[N<<1][3],q[N][3],lca[N],c[N],up[N],dp[N],st[N],i[N];  //数组太多,解释不过来QAQ
int read(){
    rg char c=getchar();
    for(;c<33;c=getchar());
    rg int f=c-48;
    for(c=getchar();(c>47)&&(c<58);c=getchar()){
        f=(f<<3)+(f<<1)+c-48;
    }
    return(f);
}
int add(int l,int x,int y){  //l编号 “从x到y的一条边”
    b[l][0]=a[x];
    b[l][1]=y;
    a[x]=l;
}
int fw(int l,int x,int y,int z){  //l编号 “第z次从x到y的LCA的一次查询”
    f[l][0]=fh[x];
    f[l][1]=y;
    f[l][2]=z;
    fh[x]=l;
}
int root(int m){
    return(s[m]?s[m]=root(s[m]):m);
}
void uni(int x,int y){
    s[x]=y;
}
void tarjan(){
    int size=1;//此处上方已有注释
    st[1]=1;
    while(size){
        if(!i[size]){
            i[size]=a[st[size]];
        }else{
            uni(b[i[size]][1],st[size]);
            i[size]=b[i[size]][0];
        }
        if(i[size]){
            dp[b[i[size]][1]]=dp[st[size]]+1;
            st[size+1]=b[i[size]][1];
            size++;
        }else{
            for(rg int i=fh[st[size]];i;i=f[i][0]){
                int get=root(f[i][1]);
                if((get!=f[i][1])||(get==st[size])){
                    lca[f[i][2]]=get;
                }
            }
            size--;     
        }
    }
}
void sg(int x,int l,int cl){ //x上面有说 l为LCA cl为修改的颜色
    for(;dp[x]>dp[l];x=now){ //一定要大于,不能修改LCA
        int now=up[x];
        if(!c[x]){
            c[x]=cl;
            up[x]=l;
        }
    }
}
int main(){
    n=read();m=read();
    for(rg int i=2;i<=n;i++){
        add(i-1,up[i]=read(),i);
    }
    for(rg int i=1;i<=m;i++){
        q[i][0]=read();q[i][1]=read();q[i][2]=read();
        fw((i<<1)-1,q[i][0],q[i][1],i);
        fw(i<<1,q[i][1],q[i][0],i);
    }
    tarjan();
    for(rg int i=m;i;i--){
        sg(q[i][0],lca[i],q[i][2]);
        sg(q[i][1],lca[i],q[i][2]);
    }
    for(rg int i=2;i<=n;i++){
        printf("%d\n",c[i]);
    }
}

猜你喜欢

转载自www.cnblogs.com/groundwater/p/12391788.html