【bzoj4551】[Tjoi2016&Heoi2016]树 离线处理+并查集

题目描述

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

输入

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

输出

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

样例输入

5 5

1 2

1 3

2 4

2 5

Q 2

C 2

Q 2

Q 5

Q 3

样例输出

1

2

2

1



题解

在线的话可以树剖,然而我选择了离线处理+并查集。

加标记比较难搞,我们可以换一种思路,先把所有标记加进来,再从后往前删掉。

每个节点的f为最近的有标记的祖先,这可以在dfs中直接实现。

然后从后往前处理,如果是修改则删标记,删为0时直接将该点的f赋为f[fa]。

如果是查询,直接记录find即可。

理论时间复杂度O(αN),然而这么慢也是醉了。

另外听说暴力可过。

#include <cstdio>
#include <algorithm>
#define N 100010
using namespace std;
int head[N] , to[N << 1] , next[N << 1] , cnt , fa[N] , t[N] , f[N] , opt[N] , p[N] , ans[N];
char str[5];
void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x , int last)
{
	if(t[x]) last = x;
	f[x] = last;
	int i;
	for(i = head[x] ; i ; i = next[i])
		if(to[i] != fa[x])
			fa[to[i]] = x , dfs(to[i] , last);
}
int find(int x)
{
	return x == f[x] ? x : f[x] = find(f[x]);
}
int main()
{
	int n , m , i , x , y;
	scanf("%d%d" , &n , &m);
	for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
	t[1] = 1;
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%s%d" , str , &p[i]);
		if(str[0] == ‘C‘) opt[i] = 1 , t[p[i]] ++ ;
	}
	dfs(1 , 0);
	for(i = m ; i >= 1 ; i -- )
	{
		if(opt[i])
		{
			t[p[i]] -- ;
			if(!t[p[i]]) f[p[i]] = f[fa[p[i]]];
		}
		else ans[i] = find(p[i]);
	}
	for(i = 1 ; i <= m ; i ++ ) if(!opt[i]) printf("%d\n" , ans[i]);
	return 0;
}
时间: 2024-10-06 16:54:59

【bzoj4551】[Tjoi2016&Heoi2016]树 离线处理+并查集的相关文章

bzoj4551[Tjoi2016&amp;Heoi2016]树

bzoj4551[Tjoi2016&Heoi2016]树 题意: 给个根节点为1的n点树,初始时节点1标记,Q个操作,每次可以标记一个点或求一个点最近一个标记了的祖先. 题解: 链剖可以写,当正解应该是并查集.离线读入所有操作,累加每个节点的标记次数,之后所有未被标记的节点向其父亲节点连边,然后倒着来,如果操作是询问则输出这个节点在并查集中的根节点,如果是标记则将该节点的标记数减1,一旦这个节点的标记数减到了0,就让它向父亲节点连边. 代码: 1 #include <cstdio> 2

【BZOJ4551】[Tjoi2016&amp;Heoi2016]树 并查集

[BZOJ4551][Tjoi2016&Heoi2016]树 Description 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个 结点,可以打多次标记.)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖 先)你能帮帮他吗? Input 输入第一行两个正整数N和Q分别表示节点个数和操作次数接下来N-1行

【bzoj5183】[Baltic2016]Park 离线+对偶图+并查集

题目描述 在Byteland的首都,有一个矩形围栏围起来的公园.在这个公园里树和访客都以一个圆形表示.公园有四个出入口,每个角落一个(1=左下角,2=右下角,3=右上角,4=左上角).访客能通过这些出入口进出公园.访客在同时碰到一个角落的两条边时就可以通过该角落进出公园.访客在公园里可以自由地移动,但他们不能和树和围栏相交.对于每个访客,给定他们进入公园的出入口,你的任务是计算他们能在哪个出入口离开公园. 输入 输入的第一行包含两个整数:n,m:树的数量和访客的数量. 第二行包含两个整数:w,h

Wikioi 2492 树状数组+并查集(单点更新区间查询)

刚开始做的时候用线段树做的,然后就跳进坑里了--因为要开方,所以区间的值都得全部变,然后想用lazy标记的,但是发现用不了,单点更新这个用不了,然后就不用了,就T了.然后实在不行了,看了别人的题解,原来是用树状数组+并查集的方法,唉--没想到啊! 因为开方之后多次那个数就会变成1了,所以是1的时候开方下去就没用了.树状数组更新的时候就把其更新的差更新即可,太机智了这题-- 昨天做了,然后出错找了好久都找不出来,原来是把s[i]写成c[i]了,然后答案一直错,晕-- #include <iostr

URAL1671 Anansi&#39;s Cobweb(离线做 + 并查集)

传送门 大意:给出一个无向图,删除Q条边,每删除一次就询问一次目前的连通块的数目. 思路:离线搞, 把删边转换为加边,每加一次边,若两个顶点不连通就用并查集把着这两个连通块合并. 代码: #include<cstdio> #include<cstring> #include<algorithm> #define MAXN 100005 using namespace std; int n, m, q; int s[MAXN], t[MAXN]; int ban[MAXN

2017端午欢乐赛——Day1T3(树的直径+并查集)

//前些天的和jdfz的神犇们联考的模拟赛.那天上午大概是没睡醒吧,考场上忘了写输出-1的情况,白丢了25分真是**. 题目描述     小C所在的城市有 n 个供电站,m条电线.相连的供电站会形成一个供电群,那么为了节省材料,供电群是一棵树的形式,也即城市是一个森林的形式(树:V个点,V-1条边的无向连通图,森林:若干棵树).每个供电群中不需要所有供电站都工作,最少只需要一个工作,其余的就都会通过电线收到电,从而完成自己的供电任务.当然,这样会产生延迟.定义两个供电站的延迟为它们之间的电线数量

hdu 5458 Stability(树链剖分+并查集)

Stability Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 1347    Accepted Submission(s): 319 Problem Description Given an undirected connected graph G with n nodes and m edges, with possibly r

BZOJ 3211 花神游历各国 (树状数组+并查集)

题解:首先,单点修改求区间和可以用树状数组实现,因为开平方很耗时间,所以在这个方面可以优化,我们知道,开平方开几次之后数字就会等于1 ,所以,用数组记录下一个应该开的数,每次直接跳到下一个不是1的数字进行开平方,至于这个数组,可以用并查集维护. #include <cstdio> #include <cmath> #include <iostream> using namespace std; typedef long long LL; LL c[100005]; in

[bzoj4551][TJOI&amp;HEOI2016]树

题目大意 一颗树,除根节点外初始都是白点,根节点是黑点. 每次染黑一个结点或者询问一个结点的最近黑色祖先. 倒序处理 倒着做,于是每次是染白一个结点. 那么就并查集搞呀! #include<cstdio> #include<algorithm> #define fo(i,a,b) for(i=a;i<=b;i++) #define fd(i,a,b) for(i=a;i>=b;i--) using namespace std; const int maxn=100000