题目:

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

实验在一个封闭的局域网内进行。局域网内有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. 【BZOJ2460】[BeiJing2011]元素 贪心+高斯消元求线性基

    [BZOJ2460][BeiJing2011]元素 Description 相传,在远古时期,位于西方大陆的 Magic Land 上,人们已经掌握了用魔法矿石炼制法杖的技术.那时人们就认识到,一个法 ...

  2. 【HTML5开发系列】表单元素

    <form> 创建一个HTML表单 属性: action 表示提交表单时浏览器应该把用户填写的数据发送到什么地方 method 用来指定表单数据发送到服务器的方式.允许值有get和post ...

  3. socket java 实例

    简单的java socket 示例 一.搭建服务器端 a).创建ServerSocket对象绑定监听端口. b).通过accept()方法监听客户端的请求. c).建立连接后,通过输入输出流读取客户端 ...

  4. iphone传感器

    传感器 什么是传感器 传感器是一种感应\检测装置, 目前已经广泛应用于智能手机上 传感器的作用 用于感应\检测设备周边的信息 不同类型的传感器, 检测的信息也不一样 iPhone中的下面现象都是由传感 ...

  5. Java语言平台

    J2SE(Java 2 Platform Standard Edition) 标准版 开发普通桌面和商务应用程序提供的解决方案,该技术体系是下面两者的基础,可以完成一些桌面应用程序的开发 J2ME(J ...

  6. SAP basis 常用事物

    1.创建一个新的用户 完成client创建和拷贝后,在开始正式工作之前,需要创建新的用户.  用这个用户进行工作.默认ddic和sap*用户不要用于实际的业务.  创建用户的过程很简单,只要以su01 ...

  7. ssm框架与shiro的整合小demo,用idea开发+maven管理

    shiro安全框架是目前为止作为登录注册最常用的框架,因为它十分的强大简单,提供了认证.授权.加密和会话管理等功能 . shiro能做什么? 认证:验证用户的身份 授权:对用户执行访问控制:判断用户是 ...

  8. C#读取excel 找不到可安装的ISAM

    实在没有办法了 就仔细的查看了 一下数据链接字符串: string strConn = "Provider=Microsoft.Jet.Oledb.4.0;Data Source=" ...

  9. 有关java之反射的使用

    1 public class Demo02 { 2 @SuppressWarnings("all") 3 public static void main(String[] args ...

  10. Kattis - yoda 【字符串】

    分析 给出两个串 从末尾开始对齐 每位对齐后,每一位 遍历 如果 第一串 的那位 < 第二串 的 那么 第一串的那位 就删去 如果 等于 两位 都保留 如果 大于 那么 保留 第二串的 那位 如 ...