zoukankan      html  css  js  c++  java
  • 【BZOJ4551】[Tjoi2016&Heoi2016]树 并查集

    【BZOJ4551】[Tjoi2016&Heoi2016]树

    Description

    在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖先)你能帮帮他吗?

    Input

    输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行,每行两个正整数u,v(1≤u,v≤n)表示u到v有一条有向边接下来Q行,形如“opernum”oper为“C”时表示这是一个标记操作,oper为“Q”时表示这是一个询问操作对于每次询问操作,1 ≤ N, Q ≤ 100000。

    Output

    输出一个正整数,表示结果

    Sample Input

    5 5
    1 2
    1 3
    2 4
    2 5
    Q 2
    C 2
    Q 2
    Q 5
    Q 3

    Sample Output

    1
    2
    2
    1

    题解:一看到题就想到树剖+线段树来搞,但是比较懒,于是决定先看discuss,然后发现人家用暴力就能过,无语~

    这题用并查集也可以做,那我们就讲讲并查集的算法

    如果我们按正常的顺序套并查集,那么我们只能将所有的点的f都指向他最近的打了标记的祖先,但这样就没法进行区间合并了

    于是我们不妨反过来删标记,先把所有打了标记的点的f都指向自己,其余的都指向它的父节点,然后从后往前处理询问。如果是删除,那么去掉该点的标记,当一个点失去所有标记时,就将它与父节点合并。如果是询问,直接find(x)就好了

    一开始竟然把q开成char了。。。

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int maxn=100010;
    int n,m,cnt;
    int to[maxn<<1],next[maxn<<1],head[maxn],fa[maxn],f[maxn],d[maxn],ans[maxn],q[maxn];
    char str[maxn][3];
    void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    void dfs(int x)
    {
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])	fa[to[i]]=x,dfs(to[i]);
    }
    int find(int x)
    {
    	return (f[x]==x)?x:(f[x]=find(f[x]));
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	int i,a,b;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	scanf("%d%d",&a,&b),add(a,b),add(b,a);
    	dfs(1);
    	d[1]=1;
    	for(i=1;i<=m;i++)
    	{
    		scanf("%s%d",str[i],&q[i]);
    		if(str[i][0]=='C')	d[q[i]]++;
    	}
    	for(i=1;i<=n;i++)	f[i]=(d[i])?i:fa[i];
    	for(i=m;i>=1;i--)
    	{
    		if(str[i][0]=='C')
    		{
    			d[q[i]]--;
    			if(!d[q[i]])	f[q[i]]=fa[q[i]];
    		}
    		else	ans[i]=find(q[i]);
    	}
    	for(i=1;i<=m;i++)	if(str[i][0]=='Q')	printf("%d
    ",ans[i]);
    	return 0;
    }
  • 相关阅读:
    和为S的连续正数序列
    数组中的逆序对
    剑指offer:数组中出现次数超过一半的数字
    剑指offer12:矩阵中的路径
    剑指offer(62):孩子们的游戏(圆圈中最后剩下的数)约瑟夫环问题
    剑指offer41:数据流中的中位数
    剑指offer56:数组中只出现一次的数字
    不用加减乘除做加法
    输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可
    求树的高度
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/6661852.html
Copyright © 2011-2022 走看看