【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询

题目描述

给出一棵n个节点的树,每一个节点开始有一个互不相同的颜色,初始根节点为1。

定义一次感染为:将指定的一个节点到根的链上的所有节点染成一种新的颜色,代价为这条链上不同颜色的数目。

现有m次操作,每次为一下三种之一:

RELEASE x:对x执行一次感染;

RECENTER x:把根节点改为x,并对原来的根节点执行一次感染;

REQUEST x:询问x子树中所有节点感染代价的平均值。

输入

输入的第一行包含两个整数n和m,分别代表局域网中计算机的数量,以及操作和询问的总数。
接下来n-1行,每行包含两个整数x和y,表示局域网中编号为x和y的计算机之间有网线直接相连。
接下来m行,每行包含一个操作或者询问,格式如问题描述中所述。

输出

对于每个询问,输出一个实数,代表平均感染时间。输出与答案的绝对误差不超过 10^(-6)时才会被视为正确。

样例输入

8 6
1 2
1 3
2 8
3 4
3 5
3 6
4 7
REQUEST 7
RELEASE 3
REQUEST 3
RECENTER 5
RELEASE 2
REQUEST 1

样例输出

4.0000000000
2.0000000000
1.3333333333



题解

LCT+树上倍增+DFS序+树状数组区间修改区间查询

这也是一道神题了。。。不查题解真的想不到。。。

首先同种颜色的一定是一条链。

然后如果把同种颜色的边看作实边,不同种颜色的边看作虚边的话,

一次感染就是LCT中的access操作,代价就是虚边数量+1。。。

(估计出题人是根据access操作的方法才出的这题吧。。。正常人根本想不到是LCT啊。。。)

一次RECENTER操作就是makeroot(makeroot自带一次access,就对应了题目的再感染一次。。。)

所以一个点的答案只与虚边数量有关,而只有虚边变化(access)时某些点的答案才会变化。

考虑加虚边答案怎么变化:一个点到其父亲节点加了一条虚边,那么其子树内所有节点到根节点都需要经过这一条虚边,该子树内答案+1。删虚边同理。

这里要注意一个问题:Splay的根节点不是这条链上深度最小的点,最左边的节点才是要更新子树的节点。因此在更新子树时还要找Splay中最左边的节点,同时不要忘了pushdown。

由于本题的根是变化的,因此参考 bzoj3083遥远的国度 ,将子树转化为dfs序上的至多两端区间,将得到的区间+1或-1。其中找第一个子节点的过程可以使用树上倍增实现。

因为本体略卡常,所以采用了树状数组区间修改区间查询,方法参考 bzoj3132上帝造题的七分钟

查询时同理,直接把区间和求出来即可。

时间复杂度$O(n\log^2n*不知多大的常数)=O(能过)$

#include <cstdio>
#include <algorithm>
#define N 100010
using namespace std;
typedef long long ll;
struct lct
{
	int fa , c[2] , rev;
}a[N];
int n , head[N] , to[N << 1] , next[N << 1] , cnt , fa[N][20] , deep[N] , pos[N] , last[N] , tot , log[N] , root = 1;
ll f[N] , g[N];
char str[15];
inline void modify(int x , int a)
{
	int i;
	for(i = x ; i <= n ; i += i & -i) f[i] += a , g[i] += x * a;
}
inline ll query(int x)
{
	int i;
	ll s1 = 0 , s2 = 0;
	for(i = x ; i ; i -= i & -i) s1 += f[i] , s2 += g[i];
	return (x + 1) * s1 - s2;
}
inline void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
	int i;
	pos[x] = ++tot;
	for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
	for(i = head[x] ; i ; i = next[i])
		if(to[i] != fa[x][0])
			fa[to[i]][0] = x , a[to[i]].fa = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
	last[x] = tot;
	modify(pos[x] , 1) , modify(last[x] + 1 , -1);
}
inline int find(int x , int y)
{
	int i;
	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
		if((1 << i) < deep[x] - deep[y])
			x = fa[x][i];
	return x;
}
inline void rever(int x)
{
	swap(a[x].c[0] , a[x].c[1]) , a[x].rev ^= 1;
}
inline void pushdown(int x)
{
	if(a[x].rev) rever(a[x].c[0]) , rever(a[x].c[1]) , a[x].rev = 0;
}
inline bool isroot(int x)
{
	return a[a[x].fa].c[0] != x && a[a[x].fa].c[1] != x;
}
void update(int x)
{
	if(!isroot(x)) update(a[x].fa);
	pushdown(x);
}
inline void rotate(int x)
{
	int y = a[x].fa , z = a[y].fa , l = (a[y].c[1] == x) , r = l ^ 1;
	if(!isroot(y)) a[z].c[a[z].c[1] == y] = x;
	a[x].fa = z , a[y].fa = x , a[a[x].c[r]].fa = y , a[y].c[l] = a[x].c[r] , a[x].c[r] = y;
}
inline void splay(int x)
{
	int y , z;
	update(x);
	while(!isroot(x))
	{
		y = a[x].fa , z = a[y].fa;
		if(!isroot(y)) rotate((a[y].c[0] == x) ^ (a[z].c[0] == y) ? x : y);
		rotate(x);
	}
}
inline void treemodify(int x , int v)
{
	if(!x) return;
	int y;
	while(a[x].c[0]) pushdown(x) , x = a[x].c[0];
	if(root == x) modify(1 , v);
	else if(pos[root] < pos[x] || pos[root] > last[x]) modify(pos[x] , v) , modify(last[x] + 1 , -v);
	else y = find(root , x) , modify(1 , v) , modify(pos[y] , -v) , modify(last[y] + 1 , v);
}
inline double treequery(int x)
{
	int y;
	if(root == x) return (double)query(n) / n;
	else if(pos[root] < pos[x] || pos[root] > last[x]) return (double)(query(last[x]) - query(pos[x] - 1)) / (last[x] - pos[x] + 1);
	else
	{
		y = find(root , x);
		return (double)(query(n) - query(last[y]) + query(pos[y] - 1)) / (n - last[y] + pos[y] - 1);
	}
}
inline void access(int x)
{
	int t = 0 , y;
	while(x) splay(x) , treemodify(t , -1) , y = a[x].c[1] , a[x].c[1] = t , treemodify(y , 1) , t = x , x = a[x].fa;
}
inline void makeroot(int x)
{
	access(x) , splay(x) , rever(x) , root = x;
}
int main()
{
	int m , i , x , y;
	scanf("%d%d" , &n , &m);
	for(i = 2 ; i <= n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x) , log[i] = log[i >> 1] + 1;
	dfs(1);
	while(m -- )
	{
		scanf("%s%d" , str , &x);
		if(str[2] == ‘L‘) access(x);
		else if(str[2] == ‘C‘) makeroot(x);
		else printf("%.10lf\n" , treequery(x));
	}
	return 0;
}
时间: 2024-12-18 02:36:10

【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询的相关文章

bzoj 3779 重组病毒 —— LCT+树状数组(区间修改+区间查询)

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3779 RELEASE操作可以对应LCT的 access,RECENTER则是 makeroot: 考虑颜色数,把一条实边变成虚边,子树+1,虚变实子树-1: 但有换根操作,怎么维护子树? 也可以用 dfs 序线段树维护,其实换 rt 只是 splay 的根方向改变,对应的子树还是可以找到的: 注意虚边变实或实边变虚时要找子树,不是直接找那个儿子,而是找那个儿子所在 splay 的根: 然后

【bzoj2819】Nim DFS序+树状数组+倍增LCA

题目描述 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.于是vfleaking决定写一个玩Nim游戏的平台来坑玩家.为了设计漂亮一点的初始局面,vfleaking用以下方式来找灵感:拿出很多石子,把它们聚成一堆一堆的,对每一堆编号1,2,3,4,...n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达.然后他不停地进行如下操

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形dp, 对于每条链u,v,w,我们只在lca(u,v)的顶点上处理它 让dp[i]表示以i为根的指数的最大值,sum[i]表示dp[vi]的和(vi为i的儿子们) 则i点有两种决策,一种是不选以i为lca的链,则dp[i]=sum[i]. 另一种是选一条以i为lca的链,那么有转移方程:dp[i]=

【(博弈)dfs序+树状数组】BZOJ2819-Nim

[题目大意] 普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略的.现在对每一堆编号1,2,3,4,...n,在堆与堆间连边,没有自环与重边,从任意堆到任意堆都只有唯一一条路径可到达.然后他不停地进行如下操作:1.随机选两个堆v,u,询问若在v到u间的路径上的石子堆中玩Nim游戏,是否有必胜策略,如果有,vfleaking将会考虑将这些石子堆作为初始局面之一,用来坑玩家. 2.把堆v中的石子数变为k. [思路

BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状数组维护, DFS到的查询点就回答询问.时间复杂度O(|ACAM|+QlogQ) ------------------------------------------------------------------------------------------- #include<cstdio>

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

[POI2007][BZOJ1103] 大都市meg|dfs序|树状数组

1103: [POI2007]大都市meg Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1350  Solved: 697[Submit][Status][Discuss] Description 在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员Blue Mary也开始骑着摩托车传递邮件了.不过,她经常回忆起以前在乡间漫步的情景.昔日,乡下有依次编号为1..n的n个小村庄,某些村庄之间有一些双向的土路.从每个村庄都恰好有一条路径到达

Codeforces Round #225 (Div. 1) C. Propagating tree dfs序+树状数组

C. Propagating tree Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/383/problem/C Description Iahub likes trees very much. Recently he discovered an interesting tree named propagating tree. The tree consists of n nodes numb