题目描述

给出一棵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;
}

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

  1. cf983E NN Country (倍增+dfs序+树状数组)

    首先可以求出从某点做$2^k$次车能到的最浅的点,这个只要dfs一下,把它的孩子能到的最浅的点更新过来就可以 然后倍增地往上跳,不能跳到lca的上面,记录坐车的次数ans 此时有三种情况(设最远能跳到 ...

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

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3779 RELEASE操作可以对应LCT的 access,RECENTER则是 makeroo ...

  3. BZOJ4999 This Problem Is Too Simple!(树上差分+dfs序+树状数组)

    对每个权值分别考虑.则只有单点加路径求和的操作.树上差分转化为求到根的路径和,子树加即可.再差分后bit即可.注意树上差分中根的父亲是0,已经忘了是第几次因为这个挂了. #include<ios ...

  4. 【bzoj4009】[HNOI2015]接水果 DFS序+树上倍增+整体二分+树状数组

    题目描述 给出一棵n个点的树,给定m条路径,每条路径有一个权值.q次询问求一个路径包含的所有给定路径中权值第k小的. 输入 第一行三个数 n和P 和Q,表示树的大小和盘子的个数和水果的个数. 接下来n ...

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

    题目描述 著名游戏设计师vfleaking,最近迷上了Nim.普通的Nim游戏为:两个人进行游戏,N堆石子,每回合可以取其中某一堆的任意多个,可以取完,但不可以不取.谁不能取谁输.这个游戏是有必胜策略 ...

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

    题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高 ...

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

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形 ...

  8. 【bzoj3881】[Coci2015]Divljak AC自动机+树链的并+DFS序+树状数组

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  9. 2018.10.20 NOIP模拟 巧克力(trie树+dfs序+树状数组)

    传送门 好题啊. 考虑前面的32分,直接维护后缀trietrietrie树就行了. 如果#号不在字符串首? 只需要维护第一个#前面的字符串和最后一个#后面的字符串. 分开用两棵trie树并且维护第一棵 ...

随机推荐

  1. SpringBoot非官方教程 | 第二十五篇:2小时学会springboot

    转载请标明出处: http://blog.csdn.net/forezp/article/details/61472783 本文出自方志朋的博客 一.什么是spring boot Takes an o ...

  2. 谈谈对bug的一点想法,说说做好日志记录的重要性

    说起程序猿,总绕不开的一个话题就是bug,估计每个程序猿听到某某测试跑过来一脸淫笑的告诉你这个功能有个bug的时候,总有种恨不得掐死他的想法.其实程序猿跟bug的关系,感觉有点像父亲和儿子的关系,自己 ...

  3. springMVC入门二

    一.准备工作 参考springMVC入门一,搭建maven项目如下: 前台结构如下: 项目介绍:使用springMVC实现前后台数据交互,例如controller返回json,页面传入pojo 二.具 ...

  4. 小白对异步IO的理解

    前言 看到越来越多的大佬都在使用python的异步IO,协程等概念来实现高效的IO处理过程,可是我对这些概念还不太懂,就学习了一下. 因为是初学者,在理解上有很多不到位的地方,如果有错误,还希望能够有 ...

  5. auto用法

    在C++11中,如果编译器在定义一个变量的时候可以推断出变量的类型,不用写变量的类型,你只需写auto即可. 第一种用法:自动推到内置类型 int x = 100; //C++ 11 auto x = ...

  6. C语言字符篇(三)字符串比较函数

    #include <string.h>   int strcmp(const char *s1, const char *s2); 比较字符串s1和s2 int strncmp(const ...

  7. source tree 使用心得

    SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端管理工具,同时也是Mercurial和Subversion版本控制系统工具.支持创建.克隆.提交.pus ...

  8. C++ 整型长度的获取 不同的系统

    不同的系统中,C++整型变量中的长度位数是不同的,通常,在老式的IBM PC中,int 的位数为16位(与short相同),而在WINDOWS XP,Win7,vax等很多其他的微型计算机中,为32位 ...

  9. P1198 [JSOI2008]最大数【树状数组】

    题目描述 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制: L 不超过当前数列的长度. (L &g ...

  10. java变量、数据类型

    public class Test { public static void main(String[] args) { //变量 //数据类型 变量名; //基本数据类型: //一个字节8位 //整 ...