洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树

正解:并查集/树链剖分+线段树

解题报告:

传送门

感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ

首先说下并查集的方法趴QwQ

首先离线,读入所有操作,然后dfs遍历整棵树,如果当前点有标记就fa[i]=i,否则fa指向父亲节点

然后倒叙查答案,每次碰到Q答案就fa[i],碰到C就把修改的那个点的标记次数--,当标记次数=0的时候fa就指向父亲节点辣

over

484很妙昂!

 

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define gc getchar()
#define t(i) edge[i].to
#define w(i) edge[i].wei
#define fy(i) edge[i].fy
#define ri register int
#define rb register bool
#define rc register char
#define rp(i,x,y) for(ri i=x;i<=y;++i)
#define my(i,x,y) for(ri i=x;i>=y;--i)
#define e(i,x) for(ri i=head[x];i;i=edge[i].nxt)

const int N=100000+10;
int n,q,ed_cnt,head[N],fa[N],fath[N],modi[N],as[N],as_cnt;
struct ed{int to,nxt;}edge[N<<1];
struct node{char op;int x;}nod[N];

il int read()
{
    rc ch=gc;ri x=0;rb y=1;
    while(ch!='-' && (ch<'0' || ch>'9'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
int fd(ri x){return fa[x]==x?x:fa[x]=fd(fa[x]);}
il char rd(){rc ch=gc;while(ch!='C' && ch!='Q')ch=gc;return ch;}
il void ad(ri x,ri y){edge[++ed_cnt]=(ed){x,head[y]};head[y]=ed_cnt;edge[++ed_cnt]=(ed){y,head[x]};head[x]=ed_cnt;}
void dfs(ri x,ri fat){fath[x]=fat;if(!modi[x])fa[x]=fat;else fa[x]=x;e(i,x)if(t(i)^fat)dfs(t(i),x);}

int main()
{
//    freopen("4092.in","r",stdin);freopen("4092.out","w",stdout);
    n=read();q=read();rp(i,1,n-1){ri x=read(),y=read();ad(x,y);}
    rp(i,1,q){char ch;cin>>ch;nod[i]=(node){ch,read()};if(nod[i].op=='C')++modi[nod[i].x];}++modi[1];
    dfs(1,0);
    my(i,q,1)
    {
        if(nod[i].op=='C'){--modi[nod[i].x];if(!modi[nod[i].x])fa[nod[i].x]=fa[fath[nod[i].x]];}
        else as[++as_cnt]=fd(nod[i].x);
    }
    my(i,as_cnt,1)printf("%d\n",as[i]);
    return 0;
}
View Code

 

然后下面港下树剖+线段树

首先树剖基操不讲

然后考虑怎么维护打标记和查询操作

就直接在线段树上维护区间中的打标记的深度max

查询就直接在链上跳,找到深度max的那个节点,输出出来就好QwQ

啊表述得不太清晰的样子,,,等下放下代码再详细讲趴QAQ

猜你喜欢

转载自www.cnblogs.com/lqsukida/p/10658205.html