zoukankan      html  css  js  c++  java
  • [BZOJ3510]首都

    luogu

    题意

    有一个(n)个点的森林,一开始一条边也没有,你要支持以下操作:
    1、连接两个点。
    2、查询一个点所在树(连通块)的重心编号。
    3、查询每棵树的重心编号的异或和。

    sol

    重心有这样的两个性质:
    1、以重心为根时每棵子树的大小不超过(frac{n}{2})
    2、两棵树合并后,重心一定在两棵树的重心的连线上。

    所以对于操作1,在(link)了两个点后,(split)原先两棵树的重心连线,这样这条连线上面的点就全部在一棵(splay)里面了。然后就在这棵(splay)上二分,每次往(size)较大的那一棵子树里面跳。
    注意重心可能会有两个,所以不能找到一个就直接退出,一定要找到(splay)的叶子节点为止。

    notice:
    1、(splay)上二分你要往下跳,就要记得(pushdown)(pushdown)
    2、向下跳完要(splay)保证复杂度啊。

    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 = 1e5+5;
    int n,m,fa[N],ch[2][N],sum[N],sz[N],rev[N],Stack[N],top,p[N],Ans;
    char s[10];
    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]]+sz[x]+1;
    }
    void reverse(int x)
    {
    	swap(ch[0][x],ch[1][x]);rev[x]^=1;
    }
    void pushdown(int x)
    {
    	if (!rev[x]) return;rev[x]=0;
    	if (ch[0][x]) reverse(ch[0][x]);if (ch[1][x]) reverse(ch[1][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)
    {
    	Stack[top=1]=x;
    	for (int y=x;!isroot(y);y=fa[y]) Stack[++top]=fa[y];
    	while (top) pushdown(Stack[top--]);
    	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);
    }
    void access(int x)
    {
    	for (int y=0;x;y=x,x=fa[x])
    	{
    		splay(x);sz[x]+=sum[ch[1][x]];
    		ch[1][x]=y;sz[x]-=sum[ch[1][x]];
    		pushup(x);
    	}
    }
    void makeroot(int x)
    {
    	access(x);splay(x);reverse(x);
    }
    void split(int x,int y)
    {
    	makeroot(x);access(y);splay(y);
    }
    int find(int x){return x==p[x]?x:p[x]=find(p[x]);}
    void link(int x,int y)
    {
    	makeroot(x);makeroot(y);
    	fa[x]=y;sz[y]+=sum[x];pushup(y);
    	x=find(x);y=find(y);
    	split(x,y);
    	int gg=n+1,now=y,ls=0,rs=0,size=sum[y]>>1;
    	while (now)
    	{
    		pushdown(now);//往下跳要pushdown啊啊啊啊
    		int lsum=ls+sum[ch[0][now]],rsum=rs+sum[ch[1][now]];
    		if (lsum<=size&&rsum<=size) gg=min(gg,now);
    		if (lsum>rsum) rs+=sum[ch[1][now]]+sz[now]+1,now=ch[0][now];
    		else ls+=sum[ch[0][now]]+sz[now]+1,now=ch[1][now];
    	}
    	splay(gg);
    	p[x]=p[y]=p[gg]=gg;Ans^=x^y^gg;
    }
    int main()
    {
    	n=gi();m=gi();
    	for (int i=1;i<=n;++i) Ans^=i,p[i]=i;
    	while (m--)
    	{
    		scanf("%s",s);int x,y;
    		if (s[0]=='X') printf("%d
    ",Ans);
    		if (s[0]=='Q') x=gi(),printf("%d
    ",find(x));
    		if (s[0]=='A') x=gi(),y=gi(),link(x,y);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Atitti 图像处理 图像混合 图像叠加 blend 原理与实现
    Atitit Gaussian Blur 高斯模糊 的原理and实现and 用途
    Atitit 图像处理 灰度图片 灰度化的原理与实现
    Atitit (Sketch Filter)素描滤镜的实现  图像处理  attilax总结
    Atitit 实现java的linq 以及与stream api的比较
    Atitit attilax在自然语言处理领域的成果
    Atitit 图像处理 常用8大滤镜效果 Jhlabs 图像处理类库 java常用图像处理类库
    Atitit 图像处理--图像分类 模式识别 肤色检测识别原理 与attilax的实践总结
    Atitit apache 和guava的反射工具
    atitit。企业的价值观 员工第一 vs 客户第一.docx
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8747943.html
Copyright © 2011-2022 走看看