题目:

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

实验在一个封闭的局域网内进行。局域网内有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操作的存在,这个集合并不一定是始终不变的。

    至此,安全机构认为已经不需要实际的实验了,于是他们拜托你编写一个程序,模拟实验的结果,并回答所有的询问。

题解:

题目真**长

我们用LCT来解决这道题。

首先我们需要观察到一个性质.每一次加入的病毒一定是新变种。

也就是说其实每个点究竟是那种颜色并不重要,因为每一次都加入新颜色

所以无论是什么颜色都会被直接xx掉。

所以我们的可以得出这样的一条结论

  • 一个点到根的不同的颜色数即为这个点到根时经过的虚边的个数

    也就是说我们直接把第一个操作当作access操作

    我们发现这样前两个操作都解决了

    但是我们查询一个点的时候并不能暴力跳fa找经过的虚边数.

    所以我们需要外部维护一下.

    由于我们要查询的是一个子树内的权和,那我们应该自然地想到用dfs序

    所以我们在进行LCT的过程中在外部动态维护一个dfs序.

Wait !!这是有换跟操作的啊,dfs序不是固定的.

我们可以根据当前的根节点rt与查询节点u的关系来分类讨论.

具体是:

if rt == u: query all
if lca(rt,u) == rt : query tree of u
if lca(u,rt) == u :
find point p has min depth and (lca(p,rt) = p,lca(p,u) = u)

上述lca是指在初始树中.

我们发现lca 只是用来祖孙判定的,我们可以用dfs序来代替这个简单的问题.

还不明白的话,,可以看我这从晚自习开始一直调到第二天早自习的代码.

如果有人想问我是怎么做到拍了一晚上没找出错交到bzoj上4msRE却只因为自己写数据生成器的时候只生成了查询操作的话我是会非常乐意地告诉你以后写数据生成器写到一半的时候不要因为有事就编译好生成器然后关掉生成器的cpp去干一些其他的愉悦的会让你忘了你的生成器还没有写完的事情比如说在大下雨天去学校满是水的塑胶跑道上去跑操并且跑完后躺在全是水的假草坪上然后会机房的时候再感个冒.

。。。 。。。

呵呵

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 410000;
const double eps = 1e-8;
inline int dcmp(const double &x){
return (x > -eps) - (x < eps);
}
int a[maxn],n,dep[maxn],rt;
namespace Graph{
struct Edge{
int to,next;
}G[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
G[++cnt].to = v;
G[cnt].next = head[u];
head[u] = cnt;
}
int ind[maxn],oud[maxn];
int dfs_clock,fa[maxn][23];
#define v G[i].to
void dfs(int u){
ind[u] = ++ dfs_clock;a[dfs_clock] = u;
for(int i = head[u];i;i=G[i].next){
if(v == fa[u][0]) continue;
dep[v] = dep[u] + 1;
fa[v][0] = u;dfs(v);
}
oud[u] = dfs_clock;
}
#undef v
}
namespace seg{
double T[maxn<<2],lazy[maxn<<2];
void build(int rt,int l,int r){
if(l == r){
T[rt] = dep[a[l]];
return ;
}
int mid = (l+r) >> 1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
T[rt] = T[rt<<1] + T[rt<<1|1];
}
inline void pushdown(int rt,int l,int r){
if(rt == 0 || dcmp(lazy[rt] == 0) ) return;
int mid = (l+r) >> 1;
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
T[rt<<1] += lazy[rt]*(mid - l + 1);
T[rt<<1|1] += lazy[rt]*(r - mid);
lazy[rt] = 0;
}
void modify(int rt,int l,int r,int L,int R,int val){
if(L <= l && r <= R){
lazy[rt] += val;
T[rt] += (r-l+1)*val;
return ;
}
int mid = (l+r) >> 1;pushdown(rt,l,r);
if(L <= mid) modify(rt<<1,l,mid,L,R,val);
if(R > mid) modify(rt<<1|1,mid+1,r,L,R,val);
T[rt] = T[rt<<1] + T[rt<<1|1];
}
void modify(int x,int val){
using namespace Graph;
if(x == rt) modify(1,1,n,1,n,val);
else if(ind[rt] < ind[x]||oud[x] < ind[rt])modify(1,1,n,ind[x],oud[x],val);
else{
int p = rt;
for(int j=20;~j;--j){
if(dep[fa[p][j]] <= dep[x]) continue;
p = fa[p][j];
}
if(1 <= ind[p] - 1) modify(1,1,n,1,ind[p]-1,val);
if(oud[p] + 1 <= n) modify(1,1,n,oud[p]+1,n,val);
}
}
double query(int rt,int l,int r,int L,int R){
if(L <= l && r <= R) return T[rt];
int mid = (l+r) >> 1;pushdown(rt,l,r);
if(R <= mid) return query(rt<<1,l,mid,L,R);
if(L > mid) return query(rt<<1|1,mid+1,r,L,R);
return query(rt<<1,l,mid,L,R) + query(rt<<1|1,mid+1,r,L,R);
}
}
namespace lct{
struct Node{
Node *ch[2],*fa;
int id,tag;
}mem[maxn],*it,*null;
inline Node* newNode(){
Node *p = it++;p->ch[0] = p->ch[1] = p->fa = null;
p->id = -1;p->tag = 0;return p;
}
inline void init(){
it = mem;null = it++;null->id = -1;
null->ch[0] = null->ch[1] = null->fa = null;
null->tag = 0;
for(int i=1;i<=n;++i) newNode()->id = i;
for(int i=2;i<=n;++i){
(mem+i)->fa = (mem+Graph::fa[i][0]);
}
}
inline void rever(Node *p){
p->tag ^= 1;swap(p->ch[0],p->ch[1]);
}
inline void pushdown(Node *p){
if(p == null || p->tag == 0) return ;
if(p->ch[0] != null) rever(p->ch[0]);
if(p->ch[1] != null) rever(p->ch[1]);
p->tag = 0;
}
inline void rotate(Node *p,Node *x){
int k = p == x->ch[1];
Node *y = p->ch[k^1],*z = x->fa;
if(z->ch[0] == x) z->ch[0] = p;
if(z->ch[1] == x) z->ch[1] = p;
if(y != null) y->fa = x;
p->fa = z;p->ch[k^1] = x;
x->fa = p;x->ch[k] = y;
}
inline bool isroot(Node *p){
return (p == null) || (p->fa->ch[0] != p && p->fa->ch[1] != p);
}
inline void splay(Node *p){
pushdown(p);
while(!isroot(p)){
Node *x = p->fa,*y = x->fa;
pushdown(y);pushdown(x);pushdown(p);
if(isroot(x)) rotate(p,x);
else if((x->ch[0] == p)^(y->ch[0] == x)) rotate(p,x),rotate(p,y);
else rotate(x,y),rotate(p,x);
}
}
inline Node* find(Node *p){
pushdown(p);
while(p->ch[0] != null){
p = p->ch[0];
pushdown(p);
}
return p;
}
inline void access(Node *x){
for(Node *y = null;x != null;y=x,x=x->fa){
splay(x);
if(x->ch[1] != null){
Node *p = find(x->ch[1]);
seg::modify(p->id,1);
}
x->ch[1] = y;
if(y != null){
Node *p = find(y);
seg::modify(p->id,-1);
}
}
}
inline void makeroot(Node *p){
access(p);splay(p);rever(p);
rt = p->id;
}
}
inline double query(int x){
using namespace Graph;
if(rt == x) return 1.0*seg::query(1,1,n,1,n)/n;
if(ind[rt] < ind[x] || oud[x] < ind[rt])
return 1.0*seg::query(1,1,n,ind[x],oud[x])/(oud[x]-ind[x]+1);
int p = rt;
for(int j=20;~j;--j){
if(dep[fa[p][j]] <= dep[x]) continue;
p = fa[p][j];
}
double upside = .0;
if(1 <= ind[p] - 1) upside += seg::query(1,1,n,1,ind[p]-1);
if(oud[p] + 1 <= n) upside += seg::query(1,1,n,oud[p]+1,n);
double dnside = (ind[p]-1) + (n-(oud[p]+1)+1);
return upside/dnside;
}
char cmd[12];
int main(){
int m;read(n);read(m);
for(int i=1,u,v;i<n;++i){
read(u);read(v);
Graph::add(u,v);
Graph::add(v,u);
}
dep[1] = 1;rt = 1;Graph::fa[1][0] = 1;
Graph::dfs(1);seg::build(1,1,n);lct::init();
for(int j=1;j<=20;++j){
for(int i=1;i<=n;++i){
Graph::fa[i][j] = Graph::fa[Graph::fa[i][j-1]][j-1];
}
}
int x;
while(m--){
scanf("%s",cmd);read(x);
if(cmd[2] == 'L'){
lct::access(lct::mem+x);
}else if(cmd[2] == 'C'){
lct::makeroot(lct::mem+x);
}else{
double ans = query(x);
printf("%.10lf\n",ans);
}
}
return 0;
}

bzoj 3779: 重组病毒 LCT+线段树+倍增的更多相关文章

  1. BZOJ 3779 重组病毒 LCT+线段树(维护DFS序)

    原题干(由于是权限题我就直接砸出原题干了,要看题意概述的话在下面): Description 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力 ...

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

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

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

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

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

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

  5. 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序

    3779: 重组病毒 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 224  Solved: 95[Submit][Status][Discuss] ...

  6. BZOJ 3779: 重组病毒(线段树+lct+树剖)

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

  7. bzoj 3779 重组病毒 好题 LCT+dfn序+线段树分类讨论

    题目大意 1.将x到当前根路径上的所有点染成一种新的颜色: 2.将x到当前根路径上的所有点染成一种新的颜色,并且把这个点设为新的根: 3.查询以x为根的子树中所有点权值的平均值. 分析 原题codec ...

  8. bzoj 3779: 重组病毒

    一道好题~~ 一个点到根传染需要的时间是这段路径上不同颜色的数目,一个点子树到根平均传染时间就是加权平均数了(好像是废话). 所以只要用线段树维护dfs序就这个可以了,换根的话一个点的子树要么在dfs ...

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

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

随机推荐

  1. vagrant系列四:vagrant搭建redis与redis的监控程序redis-stat

    上一篇php7环境的搭建 真是火爆.仅仅两天时间,就破了我之前swagger系列的一片文章,看来,大家对搭建好开发环境真是情有独钟. 为了訪问量,我今天再来一篇redis的搭建. 当然不能仅仅是red ...

  2. 纯JS实现动态时间

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. Spring Ioc (this is my first example)

    一.首先看下源码结构 二.HelloWord 类 package com.northeasttycoon.bean; /** * 打印出 helloword 参数值 * * @author tycoo ...

  4. fiddler 清除证书+重新添加证书

    1. 清除证书 ①任意浏览器,打开Internet属性弹窗,点击内容页签下<证书> ②删除个人页签下,颁发者为DO_NOT_TRUST_Fiddler**的数据 2. 重新认证证书 ①打开 ...

  5. EasyNVR流媒体直播之:零基础实现摄像头的全平台直播 (二)公网直播的实现

    接上回(https://blog.csdn.net/xiejiashu/article/details/81276870),我们实现内网直播,可以实现直播的web观看,该篇博文我们将实现公网的直播. ...

  6. 【python】-- web开发之JavaScript

    JavaScript JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理. ...

  7. java堆分析神器MAT

    Memory Analyzer(MAT) 基于Eclipse的软件 http://www.eclipse.org/mat/

  8. 我的Android进阶之旅------>Android中android:visibility 属性VISIBLE、INVISIBLE、GONE的区别

    在Android开发中,大部分控件都有visibility这个属性,其属性有3个分别为"visible "."invisible"."gone&quo ...

  9. bug-4——bootStrap中的table语言设置

    $(document).ready(function() {     $('.datatable').dataTable( {                 "Language" ...

  10. 论文解析 "A Non-Local Cost Aggregation Method for Stereo Matching"

    传统的使用窗口的方法缺陷主要在 1.窗口外的像素不能参与匹配判断. 2.在低纹理区域很容易产生错误匹配 论文的主要贡献在代价聚类上(左右图像带匹配点/区域的匹配代价计算),目标是图像内所有点都对该点传 ...