zoukankan      html  css  js  c++  java
  • [BZOJ4573][ZJOI2016]大♂森林

    bzoj
    luogu
    uoj

    sol

    (orz HJT dalao)教会我做这道题。
    考虑每两个相邻位置的树的差异。
    对于一个1操作(更换生长节点),假设区间是([l,r]),那么第(l-1)棵树和第(l)棵数就会产生一定的差异,具体来说,假设在这之后没有1操作了,那么所有应该在第(l-1)棵树中接到原生长节点下面的点都在第(l)棵树中被接到了新的生长节点的下面。
    我们考虑怎么快速实现这个操作。很显然,我们只要把修改了生长节点后新长出来的点打个包,然后(cut)(link)一下就好了。
    如果是一棵子树,直接对根节点进行操作就行了。
    但是显然并不是一棵子树啊。
    那就直接新建一个点把新建的点接在下面就好了。
    具体来说,对于每一个1操作,新建一个虚点,把所有从这个生长节点长出来的点接在下面。
    在处理到第(l)棵树时,把这个虚点接到那个生长节点的下面去;处理到第(r+1)棵树的时候再把虚点接到前一个生长节点对应的虚点上去。
    这样就可以直接查询两个点的路径长度了。
    但是有新建的虚点?
    直接把实点的权值设为1,虚点的权值设为0,然后查路径和就好了。
    但是这是一棵有根树,并不能(split)啊。
    所以可以查(s[x]+s[y]-2*s[lca(x,y)])
    现在的问题是:怎么在(LCT)(LCA)
    (access(x)),然后在(access(y))的时候,最后一个由虚边改为实边的位置就是(LCA)
    这个画个图就能理解了吧。
    然后这题就做完了。

    code

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    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;
    }
    const int N = 4e5+5;
    struct node{
    	int pos,tim,x,y;
    	bool operator < (const node &b) const
    		{return pos!=b.pos?pos<b.pos:tim<b.tim;}
    }q[N];
    int n,m,cnt,Q,fa[N],ch[2][N],val[N],sum[N],grow,real,tot,id[N],L[N],R[N],ans[N];
    bool son(int x){return x==ch[1][fa[x]];}
    bool isroot(int x)
    {
    	return x!=ch[0][fa[x]]&&x!=ch[1][fa[x]];
    }
    void pushup(int x)
    {
    	sum[x]=sum[ch[0][x]]+sum[ch[1][x]]+val[x];
    }
    void rotate(int x)
    {
    	int y=fa[x],z=fa[y],c=son(x);
    	ch[c][y]=ch[c^1][x];if (ch[c][y]) fa[ch[c][y]]=y;
    	fa[x]=z;if (!isroot(y)) ch[son(y)][z]=x;
    	ch[c^1][x]=y;fa[y]=x;pushup(y);
    }
    void splay(int x)
    {
    	for (int y=fa[x];!isroot(x);rotate(x),y=fa[x])
    		if (!isroot(y)) son(x)^son(y)?rotate(x):rotate(y);
    	pushup(x);
    }
    int access(int x)
    {
    	int y=0;
    	for (;x;y=x,x=fa[x])
    		splay(x),ch[1][x]=y,pushup(x);
    	return y;
    }
    void link(int x,int y)
    {
    	access(x);splay(x);fa[x]=y;
    }
    void cut(int x)
    {
    	access(x);splay(x);
    	ch[0][x]=fa[ch[0][x]]=0;pushup(x);
    }
    int main()
    {
    //	freopen("a.in","r",stdin);
    //	freopen("a.out","w",stdout);
    	n=gi();m=gi();link(2,1);val[1]=1;id[1]=1;
    	L[1]=1;R[1]=n;grow=tot=2;real=1;
    	for (int i=1;i<=m;++i)
    	{
    		int opt=gi(),l=gi(),r=gi();
    		if (!opt)
    		{
    			link(id[++real]=++tot,grow);val[tot]=1;
    			L[real]=l;R[real]=r;
    		}
    		if (opt==1)
    		{
    			int x=gi();l=max(l,L[x]),r=min(r,R[x]);
    			if (l>r) continue;
    			link(++tot,grow);
    			q[++cnt]=(node){l,i-m,tot,id[x]};q[++cnt]=(node){r+1,i-m,tot,grow};
    			grow=tot;
    		}
    		if (opt==2)
    		{
    			int x=gi();
    			q[++cnt]=(node){l,++Q,id[r],id[x]};
    		}
    	}
    	sort(q+1,q+cnt+1);
    	for (int i=1;i<=cnt;++i)
    		if (q[i].tim>0)
    		{
    			int res,gg;
    			access(q[i].x);splay(q[i].x);res=sum[q[i].x];
    			gg=access(q[i].y);splay(q[i].y);res+=sum[q[i].y];
    			access(gg);splay(gg);res-=sum[gg]<<1;
    			ans[q[i].tim]=res;
    		}
    		else cut(q[i].x),link(q[i].x,q[i].y);
    	for (int i=1;i<=Q;++i) printf("%d
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    selenium从入门到应用
    Maven 小技巧之 自动更新你的jar包
    selenium从入门到应用
    selenium从入门到应用
    沈逸老师PHP魔鬼特训笔记(8)
    PHP实现遍历、复制、删除目录
    沈逸老师PHP魔鬼特训笔记(7)--我叫什么名字
    PHP常用文件函数和目录函数整理
    沈逸老师PHP魔鬼特训笔记(6)--巫术与骨架
    沈逸老师PHP魔鬼特训笔记(5)
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8748048.html
Copyright © 2011-2022 走看看