【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序
3779: 重组病毒
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 224 Solved: 95
[Submit][Status][Discuss]
Description
实验在一个封闭的局域网内进行。局域网内有n台计算机,编号为1~n。一些计算机之间通过网线直接相连,形成树形的结构。局域网中有一台特殊的计算机,称之为核心计算机。根据一些初步的研究,研究员们拟定了一个一共m步的实验。实验开始之前,核心计算机的编号为1,每台计算机中都有病毒的一个变种,而且每台计算机中的变种都不相同。实验中的每一步会是下面中的一种操作:
在编号为x的计算机中植入病毒的一个新变种。这个变种在植入之前不存在于局域网中。
将核心计算机改为编号为x的计算机。但是这个操作会导致原来核心计算机中的病毒产生新变种,并感染过来。换言之,假设操作前的核心计算机编号为y,相当于在操作后附加了一次RELEASE y的操作。
根据研究的结论,在植入一个新变种时,病毒会在局域网中搜索核心计算机的位置,并沿着网络中最短的路径感染过去。
而第一轮实验揭露了一个惊人的真相:病毒的不同变种是互斥的。新变种在感染一台已经被旧变种感染的电脑时,会把旧变种完全销毁之后再感染。但研究员发现了实现过程中的漏洞。如果新变种在感染过程中尚未销毁过这类旧变种,需要先花费1单位时间分析旧变种,才能销毁。如果之前销毁过这类旧变种,就可以认为销毁不花费时间。病毒在两台计算机之间的传播亦可认为不花费时间。
3、 REQUEST x
询问如果在编号为x的计算机的关键集合中的计算机中植入一个新变种,平均感染时间为多长。编号为y的计算机在编号为x的计算机的关键集合中,当且仅当从y沿网络中的最短路径感染到核心计算机必须经过x。由于有RECENTER操作的存在,这个集合并不一定是始终不变的。
Input
接下来n-1行,每行包含两个整数x和y,表示局域网中编号为x和y的计算机之间有网线直接相连。
接下来m行,每行包含一个操作或者询问,格式如问题描述中所述。
Output
Sample Input
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
2.0000000000
1.3333333333
HINT
N < = 1 00 000 M < = 1 00 000
Source
Solution
这道题真是容易写残调半天。
把题目转化一下发现这些过程实际上类似于LinkCutTree,具体的三个操作分别是:
1、将x到根路径上的所有点染成一种新的颜色;
2、将x到根路径上的所有点染成一种新的颜色,并且把这个点设为根;
3、查询以x为根的子树中所有点到根 虚边数+1 的平均值。
然后1对应Access,2对应makeroot,3维护子树和。
LinkCutTree并不支持维护子树和,所以用dfs序+线段树维护,在每次Access的时候会对相应节点子树+1\-1,这个用线段树很好实现。
然后至于换根对线段树的影响,就一如既往的讨论三种情况即可。
自己开始写的时候LCT直接link(x,y),一直有点小错误,然后发现,我的Link是无根树的写法,此处有向树,直接Link需要注意一下谁是谁的fa,然后就可以了,也可以直接利用DFS的信息赋值,然后再另其自生自灭。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
inline int read()
{
int x=0,f=1; char ch=getchar();
while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
#define MAXN 100010
int N,M;
struct EdgeNode{int next,to,from;}edge[MAXN<<1];
int head[MAXN],cnt=1;
inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].from=u;}
inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
int pl[MAXN],dfn,pre[MAXN],pr[MAXN],root,father[17][MAXN],deep[MAXN];
LL val[MAXN];
inline void DFS(int now,int last)
{
pl[now]=++dfn; pre[dfn]=now; val[now]=val[last]+1;
for (int i=1; i<=16; i++)
if (deep[now]>=(1<<i)) father[i][now]=father[i-1][father[i-1][now]];
else break;
for (int i=head[now]; i; i=edge[i].next)
if (edge[i].to!=last)
father[0][edge[i].to]=now,
deep[edge[i].to]=deep[now]+1,
DFS(edge[i].to,now);
pr[now]=dfn;
}
inline int LCA(int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
int dd=deep[x]-deep[y];
for (int i=0; i<=16; i++) if (dd&(1<<i)) x=father[i][x];
for (int i=16; i>=0; i--)
if (father[i][x]!=father[i][y])
x=father[i][x],y=father[i][y];
return x==y? x:father[0][x];
}
inline int Jump(int x,int k)
{
for (int i=0; i<=16; i++)
if (k&(1<<i)) x=father[i][x];
return x;
}
namespace SgtTree
{
LL tree[MAXN<<2],tag[MAXN<<2]; int size[MAXN<<2];
inline void Update(int now) {tree[now]=tree[now<<1]+tree[now<<1|1];}
inline void Add(int now,LL val) {tree[now]+=(LL)size[now]*val; tag[now]+=val;}
inline void PushDown(int now)
{
if (!tag[now] || size[now]==1) return;
Add(now<<1,tag[now]); Add(now<<1|1,tag[now]);
tag[now]=0;
}
inline void Build(int now,int l,int r)
{
size[now]=r-l+1;
if (l==r) {tree[now]=val[pre[l]]; return;}
int mid=(l+r)>>1;
Build(now<<1,l,mid); Build(now<<1|1,mid+1,r);
Update(now);
}
inline void Modify(int now,int l,int r,int L,int R,LL val)
{
if (L>R) return;
PushDown(now);
if (L<=l && R>=r) {Add(now,val); return;}
int mid=(l+r)>>1;
if (L<=mid) Modify(now<<1,l,mid,L,R,val);
if (R>mid) Modify(now<<1|1,mid+1,r,L,R,val);
Update(now);
}
inline LL QueryT(int now,int l,int r,int L,int R)
{
if (L>R) return 0;
PushDown(now);
if (L<=l && R>=r) return tree[now];
int mid=(l+r)>>1; LL re=0;
if (L<=mid) re+=QueryT(now<<1,l,mid,L,R);
if (R>mid) re+=QueryT(now<<1|1,mid+1,r,L,R);
return re;
}
inline int QueryS(int now,int l,int r,int L,int R)
{
if (L>R) return 0;
PushDown(now);
if (L<=l && R>=r) return size[now];
int mid=(l+r)>>1,re=0;
if (L<=mid) re+=QueryS(now<<1,l,mid,L,R);
if (R>mid) re+=QueryS(now<<1|1,mid+1,r,L,R);
return re;
}
inline double Query(int now)
{
if (!now) return 0;
LL sum=0; int x,y,sz=0;
if (now==root)
sz=QueryS(1,1,N,pl[1],pr[1]),sum=QueryT(1,1,N,pl[1],pr[1]);
else
{
x=LCA(root,now);
if (x!=now)
sz=QueryS(1,1,N,pl[now],pr[now]),sum=QueryT(1,1,N,pl[now],pr[now]);
else
y=Jump(root,deep[root]-deep[now]-1),
sz=QueryS(1,1,N,1,pl[y]-1)+QueryS(1,1,N,pr[y]+1,N),
sum=QueryT(1,1,N,1,pl[y]-1)+QueryT(1,1,N,pr[y]+1,N);
}
return 1.0*sum/sz;
}
inline void Modify(int now,LL val)
{
if (!now) return;
int x,y;
if (now==root) Modify(1,1,N,pl[1],pr[1],val);
else
{
x=LCA(root,now);
if (x!=now)
Modify(1,1,N,pl[now],pr[now],val);
else
y=Jump(root,deep[root]-deep[now]-1),
Modify(1,1,N,1,pl[y]-1,val),Modify(1,1,N,pr[y]+1,N,val);
}
}
}using namespace SgtTree;
namespace LCT
{
int fa[MAXN],son[MAXN][2],left[MAXN],right[MAXN]; bool rev[MAXN];
inline bool is_root(int x) {return !fa[x] || son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
inline void Pushup(int x)
{
if (!x) return;
left[x]=right[x]=x;
if (son[x][0]) left[x]=left[son[x][0]];
if (son[x][1]) right[x]=right[son[x][1]];
}
inline void Rev(int x) {if (!x) return; swap(left[x],right[x]),swap(son[x][0],son[x][1]),rev[x]^=1;}
inline void Pushdown(int x) {if (!x) return; if (rev[x]) Rev(son[x][0]),Rev(son[x][1]),rev[x]^=1;}
inline void Rotate(int x)
{
int y=fa[x],w=son[y][1]==x,z=fa[y];
son[y][w]=son[x][w^1];
if (son[x][w^1]) fa[son[x][w^1]]=y;
if (son[z][0]==y) son[z][0]=x; else if (son[z][1]==y) son[z][1]=x;
fa[x]=z; fa[y]=x; son[x][w^1]=y; Pushup(y);
}
int stack[MAXN];
inline void Splay(int x)
{
int t=x,top=0,y; stack[++top]=x;
while (!is_root(t)) stack[++top]=t=fa[t];
while (top) Pushdown(stack[top--]);
while (!is_root(x))
{
y=fa[x];
if (!is_root(y))
if ((son[fa[y]][0]==y)^(son[y][0]==x)) Rotate(x);
else Rotate(y);
Rotate(x);
}
Pushup(x);
}
inline void Access(int x)
{
for (int y=0; x; y=x,x=fa[x])
Splay(x),
SgtTree::Modify(left[y],-1),
SgtTree::Modify(left[son[x][1]],1),
son[x][1]=y,Pushup(x);
}
inline void Makeroot(int x) {Access(x); Splay(x); Rev(x);}
}using namespace LCT;
int main()
{
N=read(),M=read();
for (int i=1,x,y; i<=N-1; i++) x=read(),y=read(),InsertEdge(x,y);
DFS(root=1,0);
SgtTree::Build(1,1,N);
for (int i=1; i<=N; i++) LCT::fa[i]=father[0][i],LCT::left[i]=LCT::right[i]=i;
while (M--)
{
char opt[10]; int x;
scanf("%s",opt+1); x=read();
switch (opt[3])
{
case 'L': LCT::Access(x); break;
case 'C': LCT::Makeroot(x); root=x; break;
case 'Q': printf("%.10lf\n",SgtTree::Query(x)); break;
}
}
return 0;
}
突然想起char哥说这种题做的非常爽...然而,我因为线段树区间操作手贱达成了if (l==r)外加LCT中忘swap了一个地方两个脑残错误调了整整两节晚自习啊,感觉非常蛋疼;
这说明以后写数据结构,思路一定要清晰,写的时候一定不能图快,要不然Debug浪费的时间更多。
外加,自己常数怎么这么大啊,BZOJ成功倒数rank1......知乎
【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序的更多相关文章
- BZOJ 3779 重组病毒 LCT+线段树(维护DFS序)
原题干(由于是权限题我就直接砸出原题干了,要看题意概述的话在下面): Description 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力 ...
- bzoj 3779: 重组病毒 LCT+线段树+倍增
题目: 黑客们通过对已有的病毒反编译,将许多不同的病毒重组,并重新编译出了新型的重组病毒.这种病毒的繁殖和变异能力极强.为了阻止这种病毒传播,某安全机构策划了一次实验,来研究这种病毒. 实验在一个封闭 ...
- BZOJ 3779 重组病毒 ——LCT 线段树
发现操作一很像一个LCT的access的操作. 然后答案就是路径上的虚边的数量. 然后考虑维护每一个点到根节点虚边的数量, 每次断开一条偏爱路径的时候,子树的值全部+1, 连接一条偏爱路径的时候,子树 ...
- bzoj 3779 重组病毒 好题 LCT+dfn序+线段树分类讨论
题目大意 1.将x到当前根路径上的所有点染成一种新的颜色: 2.将x到当前根路径上的所有点染成一种新的颜色,并且把这个点设为新的根: 3.查询以x为根的子树中所有点权值的平均值. 分析 原题codec ...
- Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序
题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s 内存限制:512.0MB 总提交次数:196 AC次数:65 平均分: ...
- BZOJ_3252_攻略_线段树+dfs序
BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...
- 【XSY2534】【BZOJ4817】树点涂色 LCT 倍增 线段树 dfs序
题目大意 Bob有一棵\(n\)个点的有根树,其中\(1\)号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜 ...
- 【bzoj4817】树点涂色 LCT+线段树+dfs序
Description Bob有一棵n个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同.定义一条路 径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色. ...
- S - Query on a tree HDU - 3804 线段树+dfs序
S - Query on a tree HDU - 3804 离散化+权值线段树 题目大意:给你一棵树,让你求这棵树上询问的点到根节点直接最大小于等于val的长度. 这个题目和之前写的那个给你一棵 ...
随机推荐
- DevExpress v16.1.5图表、Dashboard等多个控件API发生变化
Dashboard # BC3835: WinForms Dashboard Designer - ChartSeriesTypeGalleryItem bar item停用 在v16.1.5之前,D ...
- iOS多线程之7.NSOperation的初识
NSOperation和GCD一样,不用我们管理线程的生命周期,加锁等问题,只要把操作封装进NSOperation中,系统会自动帮我们创建线程,执行操作.而且他是面向对象的,我们看起来更容易理解,使用 ...
- Log4j记录日志步骤
记录日志对调试Bug很有帮助 亲身体会 个人习惯用Log4J,大家可以在apache网站:jakarta.apache.org/log4j 可以免费下载到Log4j最新版本的软件包. Log4j支持 ...
- 菜单(Menu)的三中创建方式——Android开发之路2
菜单的三种创建方式 一.OptionsMenu---选项菜单 Android应用中的菜单默认是隐藏的,只有当用户点击手机上的MENU键,系统才会显示菜单.这种菜单叫做选项菜单(Options Menu ...
- 看看C# 6.0中那些语法糖都干了些什么(终结篇)
终于写到终结篇了,整个人像在梦游一样,说完这一篇我得继续写我的js系列啦. 一:带索引的对象初始化器 还是按照江湖老规矩,先扒开看看到底是个什么玩意. 1 static void Main(strin ...
- MTU(Maximum transmission unit) 最大传输单元
最大传输单元(Maximum transmission unit),以太网MTU为1500. 不同网络MTU如下: 如果最大报文数据大小(MSS)超过MTU,则会引起分片操作. 路径MTU: 网路 ...
- 动态调用WebService
WebService内容 using Microsoft.CSharp;using System;using System.CodeDom;using System.CodeDom.Compiler; ...
- redis数据结构存储Dict设计细节(redis的设计与实现笔记)
说到redis的Dict(字典),虽说算法上跟市面上一般的Dict实现没有什么区别,但是redis的Dict有2个特殊的地方那就是它的rehash(重新散列)和它的字典节点单向链表. 以下是dict用 ...
- 封装系统(以封装Windows 7为例)
安装步骤: 1.安装系统 2.启用Administrator帐户 3.进行简单的系统设置 4.系统精简 5.安装Adobe Flash Player 6.设置IE主页 7.在系统盘(C盘)创建Sysp ...
- RedHat 和 Mirantis OpenStack 产品的版本和功能汇总和对比(持续更新)
Mirantis 和 Red Hat 作为 OpenStack 商业化产品领域的两大领军企业,在行业内有重要的地位.因此,研究其产品版本发布周期和所支持的功能,对制定 OpenStack 产品的版本和 ...