原题干(由于是权限题我就直接砸出原题干了,要看题意概述的话在下面):

Description

黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒。这种病毒的繁殖和变异能力极强。为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒。
实验在一个封闭的局域网内进行。局域网内有n台计算机,编号为1~n。一些计算机之间通过网线直接相连,形成树形的结构。局域网中有一台特殊的计算机,称之为核心计算机。根据一些初步的研究,研究员们拟定了一个一共m步的实验。实验开始之前,核心计算机的编号为1,每台计算机中都有病毒的一个变种,而且每台计算机中的变种都不相同。实验中的每一步会是下面中的一种操作:
1、 RELEASE x
在编号为x的计算机中植入病毒的一个新变种。这个变种在植入之前不存在于局域网中。
2、 RECENTER x
将核心计算机改为编号为x的计算机。但是这个操作会导致原来核心计算机中的病毒产生新变种,并感染过来。换言之,假设操作前的核心计算机编号为y,相当于在操作后附加了一次RELEASE y的操作。
根据研究的结论,在植入一个新变种时,病毒会在局域网中搜索核心计算机的位置,并沿着网络中最短的路径感染过去。
而第一轮实验揭露了一个惊人的真相:病毒的不同变种是互斥的。新变种在感染一台已经被旧变种感染的电脑时,会把旧变种完全销毁之后再感染。但研究员发现了实现过程中的漏洞。如果新变种在感染过程中尚未销毁过这类旧变种,需要先花费1单位时间分析旧变种,才能销毁。如果之前销毁过这类旧变种,就可以认为销毁不花费时间。病毒在两台计算机之间的传播亦可认为不花费时间。
研究员对整个感染过程的耗时特别感兴趣,因为这是消灭病毒的最好时机。于是在m步实验之中,研究员有时还会做出如下的询问:
3、 REQUEST x
询问如果在编号为x的计算机的关键集合中的计算机中植入一个新变种,平均感染时间为多长。编号为y的计算机在编号为x的计算机的关键集合中,当且仅当从y沿网络中的最短路径感染到核心计算机必须经过x。由于有RECENTER操作的存在,这个集合并不一定是始终不变的。
至此,安全机构认为已经不需要实际的实验了,于是他们拜托你编写一个程序,模拟实验的结果,并回答所有的询问。

Input

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

Output

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

Sample Input

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

Sample Output

4.0000000000
2.0000000000
1.3333333333

HINT

N<=100000,M<=100000.

题意概述:给出一棵树,初始每个结点有不同的颜色。现在支持三种操作:1.把某个结点的颜色改成一个之前都没有出现过的颜色,并将这个点到当前树根路径上的所有点全部改成这个颜色;2.改变当前的树根到另外一个点,并对原来的树根进行一次操作1;3.把询问当前形态的树中对一个点的子树中所有点进行操作1的平均代价(操作1代价的计算方式:这个点到当前树根路径上的不同颜色数量)。

从原题干来分析,相同的颜色一定连续出现在一条链上。再仔细想想,这样的操作正是LCT中的access操作!而每一次操作的代价就是access的时候经历的虚边的数量。如果是单点询问的话这就是一道LCT的裸题,但是问题在于题目要求的是子树询问。对子树信息的维护很容易想到用DFS序配合线段树来搞定。(于是就有两种流派:线段树单独在外面维护子树信息和就在LCT中维护子树信息两种,我选择线段树,常数小啊!如果要Link和Cut的话大不了我再来个LCT维护一下DFS序。。。)

分析没有换根操作的情况,在access操作中,令函数调用的时候的对象为s,当前所在结点为x,上一次所在的splay的根结点为y。y所在的splay就是s->y所在splay权值(真实树中深度)最小的点的一条链。我们找到链顶,可以发现这个链顶的子树中所有及结点在这一次access跳跃之后答案都要-1(每个点到根上的路径都少了一种颜色),x在当前树中位于splay维护的链上的儿子的子树中所有结点的答案+1。一路维护上去所有被影响的点的答案都被更新了。

换根的情况?可以发现在没有换根操作的时候可以保证对每个点的子树进行操作的时候一定是对应dfs序中一段连续的序列,但是换根之后就可以出现当前某个点子树在以1为根的dfs序中序列不连续,但是实际上也可以发现就是被分成了两端,x的子树对应的dfs序就是把x当前的父亲从dfs序中挖掉剩下的部分。对于是不是要挖点的判断借助DFS序就可以做到了。

随便分析几下思路就有了,也不是很难码,关键是细节,各种情况考虑全(为了节省您的时间请一定把一切想清楚了再开始码)。。。。。。这里只说一点:注意对LCT每个点的儿子的正确维护!

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
const int MAXN=;
typedef long long LL; int N,M,root_now;
struct edge{ int to,next; }E[MAXN<<];
int first[MAXN],np,val[MAXN],dfs_clock,l[MAXN],r[MAXN];
struct segment_tree{
static const int maxn=;
int rt,np,lc[maxn],rc[maxn]; LL sum[maxn],add[maxn];
void pushup(int now) { sum[now]=sum[lc[now]]+sum[rc[now]]; }
void pushdown(int now,int L,int R)
{
if(!add[now]) return;
int m=L+R>>;
add[lc[now]]+=add[now],sum[lc[now]]+=add[now]*(m-L+);
add[rc[now]]+=add[now],sum[rc[now]]+=add[now]*(R-m);
add[now]=;
}
void build(int &now,int L,int R,int *a)
{
now=++np;
lc[now]=rc[now]=sum[now]=add[now]=;
if(L==R){ sum[now]=a[L]; return;}
int m=L+R>>;
build(lc[now],L,m,a); build(rc[now],m+,R,a);
pushup(now);
}
void update(int now,int L,int R,int A,int B,int v)
{
if(A<=L&&R<=B){
add[now]+=v,sum[now]+=(R-L+)*v;
return;
}
pushdown(now,L,R);
int m=L+R>>;
if(B<=m) update(lc[now],L,m,A,B,v);
else if(A>m) update(rc[now],m+,R,A,B,v);
else update(lc[now],L,m,A,B,v),update(rc[now],m+,R,A,B,v);
pushup(now);
}
LL query(int now,int L,int R,int A,int B)
{
if(A<=L&&R<=B) return sum[now];
pushdown(now,L,R);
int m=L+R>>;
if(B<=m) return query(lc[now],L,m,A,B);
if(A>m) return query(rc[now],m+,R,A,B);
return query(lc[now],L,m,A,B)+query(rc[now],m+,R,A,B);
}
}st;
struct link_cut_tree{
static const int maxn=;
struct node{
int fa,ch[]; bool rev;
node(){ fa=ch[]=ch[]=,rev=; }
}nd[maxn];
void link(int x,int d,int y) { nd[x].ch[d]=y,nd[y].fa=x; }
bool isrt(int x) { return nd[nd[x].fa].ch[]!=x&&nd[nd[x].fa].ch[]!=x; }
void pushdown(int x)
{
if(!nd[x].rev) return;
int lc=nd[x].ch[],rc=nd[x].ch[];
if(lc) nd[lc].rev^=,swap(nd[lc].ch[],nd[lc].ch[]);
if(rc) nd[rc].rev^=,swap(nd[rc].ch[],nd[rc].ch[]);
nd[x].rev=;
}
void rot(int x)
{
int y=nd[x].fa,z=nd[y].fa;
pushdown(y);pushdown(x);
int d=nd[y].ch[]==x;
if(!isrt(y)) link(z,y==nd[z].ch[],x); nd[x].fa=z;
link(y,d^,nd[x].ch[d]);
link(x,d,y);
}
void splay(int x)
{
pushdown(x);
while(!isrt(x)){
int y=nd[x].fa,z=nd[y].fa;
if(!isrt(y)) rot((x==nd[y].ch[])==(y==nd[z].ch[])?y:x);
rot(x);
}
}
int find(int x,int d)
{
while(nd[x].ch[d]){ pushdown(x),x=nd[x].ch[d]; }
return x;
}
void access(int x)
{
int y=,xx,yy;
while(x){
splay(x);
if(y){
yy=find(y,);
if(l[x]<l[yy]&&l[yy]<=r[x]) st.update(st.rt,,N,l[yy],r[yy],-);
else{
if(l[x]>) st.update(st.rt,,N,,l[x]-,-);
if(r[x]<N) st.update(st.rt,,N,r[x]+,N,-);
}
}
pushdown(x);
if((xx=find(nd[x].ch[],))&&l[x]<l[xx]&&l[xx]<=r[x]) st.update(st.rt,,N,l[xx],r[xx],);
else if(xx){
if(l[x]>) st.update(st.rt,,N,,l[x]-,);
if(r[x]<N) st.update(st.rt,,N,r[x]+,N,);
}
nd[x].ch[]=y,y=x,x=nd[x].fa;
}
}
void mroot(int x)
{
access(x); splay(x);
nd[x].rev^=,swap(nd[x].ch[],nd[x].ch[]);
}
}lct; void add_edge(int u,int v)
{
E[++np]=(edge){v,first[u]};
first[u]=np;
}
void data_in()
{
scanf("%d%d",&N,&M);
int x,y;
for(int i=;i<N;i++){
scanf("%d%d",&x,&y);
add_edge(x,y); add_edge(y,x);
}
}
void DFS(int i,int f,int d)
{
l[i]=++dfs_clock,val[l[i]]=d;
for(int p=first[i];p;p=E[p].next){
int j=E[p].to;
if(j==f) continue;
lct.nd[j].fa=i;
DFS(j,i,d+);
}
r[i]=dfs_clock;
}
void work()
{
DFS(root_now=,,);
st.build(st.rt,,N,val);
char op[]; int x,len; double ans;
for(int i=;i<=M;i++){
scanf("%s%d",op,&x);
if(op[]=='L') lct.access(x);
else if(op[]=='C') lct.mroot(root_now=x);
else if(op[]=='Q'){
ans=,len=;
if(l[x]<=l[root_now]&&r[root_now]<=r[x]){
lct.splay(x);
x=lct.nd[x].ch[]?lct.find(lct.nd[x].ch[],):lct.nd[x].fa;
if(l[x]>) ans+=1.0*st.query(st.rt,,N,,l[x]-),len+=l[x]-;
if(r[x]<N) ans+=1.0*st.query(st.rt,,N,r[x]+,N),len+=N-r[x];
ans/=len;
}
else ans=1.0*st.query(st.rt,,N,l[x],r[x])/(r[x]-l[x]+);
printf("%.10f\n",ans);
}
}
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
data_in();
work();
return ;
}

BZOJ 3779 重组病毒 LCT+线段树(维护DFS序)的更多相关文章

  1. BZOJ 3779 重组病毒 ——LCT 线段树

    发现操作一很像一个LCT的access的操作. 然后答案就是路径上的虚边的数量. 然后考虑维护每一个点到根节点虚边的数量, 每次断开一条偏爱路径的时候,子树的值全部+1, 连接一条偏爱路径的时候,子树 ...

  2. bzoj 3779: 重组病毒 LCT+线段树+倍增

    题目: 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒. 实验在一个封闭 ...

  3. bzoj 3779: 重组病毒【LCT+线段树维护dfs序】

    %.8lf会WA!!%.8lf会WA!!%.8lf会WA!!要%.10lf!! 和4817有点像,但是更复杂. 首先对于操作一"在编号为x的计算机中植入病毒的一个新变种,在植入一个新变种时, ...

  4. BZOJ 4817 [SDOI2017]树点涂色 (LCT+线段树维护dfs序)

    题目大意:略 涂色方式明显符合$LCT$里$access$操作的性质,相同颜色的节点在一条深度递增的链上 用$LCT$维护一个树上集合就好 因为它维护了树上集合,所以它别的啥都干不了了 发现树是静态的 ...

  5. CF877E Danil and a Part-time Job 线段树维护dfs序

    \(\color{#0066ff}{题目描述}\) 有一棵 n 个点的树,根结点为 1 号点,每个点的权值都是 1 或 0 共有 m 次操作,操作分为两种 get 询问一个点 x 的子树里有多少个 1 ...

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

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

  7. P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA

    \(\color{#0066ff}{ 题目描述 }\) Bob有一棵\(n\)个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点 ...

  8. CodeForces 343D 线段树维护dfs序

    给定一棵树,初始时树为空 操作1,往某个结点注水,那么该结点的子树都注满了水 操作2,将某个结点的水放空,那么该结点的父亲的水也就放空了 操作3,询问某个点是否有水 我们将树进行dfs, 生成in[u ...

  9. bzoj 3779 重组病毒——LCT维护子树信息

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3779 调了很久……已经懒得写题解了.https://www.cnblogs.com/Zinn ...

随机推荐

  1. springboot 整合dubbo 消费模块引入springboot 之后自动注入jdbc 模块导致启动报错问题

    方案一: 排除方法 pom文件直接将数据起步模块内排除数据源自动注入 jdbc jar <!--mybatis-plus 集成 --><!--mybitis--><dep ...

  2. 解决model属性与系统重名

    .h .m + (NSDictionary *)replacedKeyFromPropertyName { return @{ @"detailId" : @"id&qu ...

  3. iOS之出现( linker command failed with exit code 1)错误总结

    本文出自:http://blog.csdn.NET/hengshujiyi/article/details/21182813 补充:我出现这个错误是一个工程中有两个同名的文件,只要删除掉一个就好了,可 ...

  4. Plupload使用API

    Plupload有以下功能和特点: 1.拥有多种上传方式:HTML5.flash.silverlight以及传统的<input type=”file” />.Plupload会自动侦测当前 ...

  5. C++新闻检索类

    研究长字符串快速全文检索技术,实现某电力公司新闻中心新闻稿件全文检索统计系统. 1. 设计实现适合新闻稿件的基础类库 2. 新闻稿件全文检索功能实现 3. 新闻稿件按照关键字统计查询   代码如下 P ...

  6. QQ群排名优化到霸屏的策略怎么做?

    谈起QQ群排名霸屏,首先要弄清楚概念,有些刚接触QQ群的朋友可能不太了解,所谓的QQ群排名霸屏,就是指当你的客户群体搜索QQ群某个关键词时,出现在QQ群搜索结果前面的群,全部或者大部分都是我们自己的群 ...

  7. struts2学习

    struts2是一种基于mvc模式的轻量级web框架,它本质上相当于一个servlet,在mvc设计模式中,struts2作为控制器(Controller)来建立模型与视图的数据交互,struts2采 ...

  8. python递归函数(计算阶乘)

    def f1(x,x1=1): if x == 1: return x1 #x1这个值为我们所需要的值,所以返回 x1 *= x r = f1(x-1,x1) #r接收返回值,并在下面接着返回 ret ...

  9. 003---socket介绍

    socket介绍 什么是socket? socket是应用层与tcp/ip协议族通信的中间软件抽象层,它是一组接口.在设计模式中.其实就是一个门面模式.我们无需深入理解tcp/udp协议,socket ...

  10. 015---Django的forms组件

    Django form表单   Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用 ...