bzoj4817: [Sdoi2017]树点涂色
题目链接
题解
数据结构.....大概很容易看出是道lct 然后弃疗
操作1很想lct里面的access操作
那么对于操作2
设F[i]=i点到lct根路径上的splay数(也就是虚边数)+1
那么对于操作2的(x,y)
ans(x,y)=F[x]+F[y]-(F(lca(x,y)))+1;
对于操作3的(x),就是在x的子树中取max,我们可以用dfs序+线段树维护
考虑操作1对操作3的影响
在access的时候,当一个边由虚变实,此时该边所连的深度大的点的颜色种类-1
反之当一边由实变虚,此时该边所连的深度大的点的颜色种类+1
trick:保存当前节点在树中最左儿子的编号用以修改区间(即left[])
ps:中途电脑爆炸,然后重码QAQ,心态爆炸
代码
/*
*
* 数据结构+LCT+SEG_TREE
*
*/
#include<cstdio>
#include<algorithm>
const int maxn = 100007;
int n,m;
struct node{
int v,next;
}edge[maxn<<1];
int head[maxn],num;
void Add_Edge(int u,int v) {
edge[++num].v=v;edge[num].next=head[u];head[u]=num;
}
int idfn[maxn],ldfn[maxn],rdfn[maxn],f[maxn][20],dep[maxn];
int cnt=0,fa[maxn];
void dfs(int u,int F) {
idfn[ldfn[u]=++cnt]=u,f[u][0]=fa[u]=F;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].v;
if(v!=F) dep[v]=dep[u]+1,dfs(v,u);
}
rdfn[u]=cnt;
return ;
}
int LCA(int x,int y) {
if(dep[x]<dep[y]) std::swap(x,y);
for(int i=dep[x]-dep[y],j=0;i;i>>=1,++j)
if(i&1)x=f[x][j];
if(x==y) return x;
for(int i=19;~i;--i)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
class Segment_Tree {
#define lc x<<1
#define rc x<<1|1
private :
struct Node {
int max,tag;
Node () : tag(0){}
};
Node t[maxn<<2];
void update(int x) {
t[x].max=std::max(t[lc].max,t[rc].max);
}
void push_down(int x) {
if(!t[x].tag)return;
int k=t[x].tag;
t[lc].max+=k;
t[lc].tag+=k;
t[rc].max+=k;
t[rc].tag+=k;
t[x].tag=0;
return ;
}
public :
void build (int x,int l,int r) {
if(l==r) {
t[x].max=dep[idfn[l]]+1;return;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
return update(x);
}
void modify(int x,int l,int r,int L,int R,int w) {
if(L<=l&&R>=r) {
t[x].tag+=w;t[x].max+=w;
return ;
}
push_down(x);
int mid=l+r>>1;
if(mid>=L) modify(lc,l,mid,L,R,w);
if(mid<R) modify(rc,mid+1,r,L,R,w);
return update(x);
}
int query(int x,int l,int r,int L,int R) {
if(L<=l&&R>=r)
return t[x].max;
push_down(x);
int mid=l+r>>1,ans=0;
if(mid>=L) ans=std::max(ans,query(lc,l,mid,L,R));
if(mid<R) ans=std::max(ans,query(rc,mid+1,r,L,R));
return ans;
}
#undef lc
#undef rc
}SEG_T;
class Link_Cut_tree {
#define lc ch[x][0]
#define rc ch[x][1]
private :
int ch[maxn][2],left[maxn];
void update(int x) {
left[x]=lc ? left[lc]:x;
}
bool isroot(int x) {
return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
void rotate(int x) {
int y=fa[x],z=fa[y],d=(ch[y][1]==x)^1;
if(!isroot(y)) ch[z][ch[z][1]==y]=x;fa[x]=z;
ch[y][d^1]=ch[x][d],fa[ch[x][d]]=y;
ch[x][d]=y;fa[y]=x;
update(y),update(x);
}
void splay(int x) {
while(!isroot(x)) {
int y=fa[x],z=fa[y];
if(!isroot(y)) {
if(ch[y][1]==x^ch[z][1]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
public:
void init() {
dfs(1,0),SEG_T.build(1,1,n);
for(int i=1;i<=n;++i) left[i]=i;
for(int j=1;j<20;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
}
void access(int x) {
for(int t=0;x;x=fa[t=x]) {
splay(x);
if(rc) SEG_T.modify(1,1,n,ldfn[left[rc]],rdfn[left[rc]],1);
rc=t;
if(rc) SEG_T.modify(1,1,n,ldfn[left[rc]],rdfn[left[rc]],-1);
}
return ;
}
int query(int x) {
int ans=0;
for(;x;x=fa[x],ans++)splay(x);
return ans;
}
int query(int u,int v) {
return query(u)+query(v)-2*query(LCA(u,v))+1;
}
#undef lc
#undef rc
}LCT;
int main() {
// freopen("001.out","w",stdout);
scanf("%d%d",&n,&m) ;
for(int a,b,i=1;i<n;++i) {
scanf("%d%d",&a,&b);
Add_Edge(a,b);
Add_Edge(b,a);
}
LCT.init();
for(int opt,x,y,i=1;i<=m;++i) {
scanf("%d",&opt);
if(opt==1) scanf("%d",&x),LCT.access(x);
if(opt==2) scanf("%d%d",&x,&y),printf("%d\n",LCT.query(x,y));
else if(opt==3) scanf("%d",&x),printf("%d\n",SEG_T.query(1,1,n,ldfn[x],rdfn[x]));
}
return 0;
}