3779: 重组病毒

44 篇文章 0 订阅
13 篇文章 0 订阅

3779: 重组病毒

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 315   Solved: 124
[ Submit][ Status][ Discuss]

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 < = 1 00 000 M < = 1 00 000


Source

[ Submit][ Status][ Discuss]

先不考虑换根操作,给树上每条边附上一个权值
如果相邻的点的颜色不同,权值为1,否则权值为0
定义一个点到根的距离为路径上边权和+1,那么询问就等价于查询子树路径平均值
对于原树构造一棵LCT,那么染色操作就等价于Access操作了
在LCT中树边的种类转换恰好就对应着原树中边权的转换
对原树dfs一遍得到dfs序,并用线段树维护区间和,这样修改操作就很简单了
如果有换根,那么就用dfs序的性质讨论一下就行了,对应需要修改的区间不多
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = 1E5 + 10;
const int T = 4;
const int N = 17;
typedef double DB;
typedef long long LL;
 
int n,m,dfs_clock,rt = 1,tp,ch[maxn][2],fa[maxn],pfa[maxn],rev[maxn]
    ,stk[maxn],L[maxn],Name[maxn],dfn[maxn],out[maxn],tfa[maxn][N],Add[maxn*T];
LL sum[maxn*T]; char s[20];
 
vector <int> v[maxn];
 
int LCA(int p,int q)
{
    if (L[p] < L[q]) swap(p,q);
    for (int i = N - 1; i >= 0; i--)
        if (L[p] - (1 << i) >= L[q])
            p = tfa[p][i];
    if (p == q) return p;
    for (int i = N - 1; i >= 0; i--)
        if (tfa[p][i] != tfa[q][i])
            p = tfa[p][i],q = tfa[q][i];
    return tfa[p][0];
}
 
int Quickfa(int x,int y)
{
    for (int now = 0; y; y >>= 1,now++)
        if (y & 1) x = tfa[x][now];
    return x;
}
 
void Push(int o,int l,int r)
{
    if (!Add[o]) return;
    sum[o] += 1LL * (r - l + 1) * Add[o];
    if (l == r) {Add[o] = 0; return;}
    Add[o<<1] += Add[o]; Add[o<<1|1] += Add[o]; Add[o] = 0;
}
 
void Build(int o,int l,int r)
{
    if (l == r) {sum[o] = L[Name[l]]; return;}
    int mid = l + r >> 1;
    Build(o<<1,l,mid); Build(o<<1|1,mid+1,r);
    sum[o] = sum[o<<1] + sum[o<<1|1];
}
 
void Modify(int o,int l,int r,int ql,int qr,int k)
{
    if (ql <= l && r <= qr) {Add[o] += k; Push(o,l,r); return;}
    int mid = l + r >> 1; Push(o,l,r);
    if (ql <= mid) Modify(o<<1,l,mid,ql,qr,k); else Push(o<<1,l,mid);
    if (qr > mid) Modify(o<<1|1,mid+1,r,ql,qr,k); else Push(o<<1|1,mid+1,r);
    sum[o] = sum[o<<1] + sum[o<<1|1];
}
 
LL Query(int o,int l,int r,int ql,int qr)
{
    Push(o,l,r);
    if (ql <= l && r <= qr) return sum[o];
    int mid = l + r >> 1; LL ret = 0;
    if (ql <= mid) ret = Query(o<<1,l,mid,ql,qr);
    if (qr > mid) ret += Query(o<<1|1,mid+1,r,ql,qr);
    return ret;
}
 
void pushdown(int x)
{
    if (!rev[x]) return;
    swap(ch[x][0],ch[x][1]);
    for (int i = 0; i < 2; i++)
        if (ch[x][i]) rev[ch[x][i]] ^= 1;
    rev[x] = 0;
}
 
void rotate(int x)
{
    int y = fa[x],z = fa[y];
    pfa[x] = pfa[y]; pfa[y] = 0;
    int d = ch[y][0] == x ? 0 : 1;
    ch[y][d] = ch[x][d^1]; fa[ch[y][d]] = y;
    ch[x][d^1] = y; fa[y] = x; fa[x] = z;
    if (z) ch[z][ch[z][1] == y] = x;
}
 
void splay(int x)
{
    for (int z = x; z; z = fa[z]) stk[++tp] = z;
    while (tp) pushdown(stk[tp--]);
    for (int y = fa[x]; y; rotate(x),y = fa[x])
        if (fa[y]) rotate((ch[y][0] == x) ^ (ch[fa[y]][0] == y) ? x : y);
}
 
void Change(int x,int y,int k)
{
    int lca = LCA(x,y);
    if (x == y) puts("Wrong!");
    if (lca == x)
    {
        y = Quickfa(y,L[y] - L[x] - 1);
        if (dfn[y] > 1) Modify(1,1,n,1,dfn[y] - 1,k);
        if (out[y] < n) Modify(1,1,n,out[y] + 1,n,k);
    }
    else Modify(1,1,n,dfn[x],out[x],k);
}
 
int FindRoot(int x)
{
    for ( ; ; x = ch[x][0])
    {
        pushdown(x);
        if (!ch[x][0]) break;
    }
    return x;
}
 
void Access(int x)
{
    for (int u = 0; x; u = x,x = pfa[x])
    {
        splay(x);
        if (ch[x][1])
        {
            fa[ch[x][1]] = 0; pfa[ch[x][1]] = x;
            Change(FindRoot(ch[x][1]),rt,1);
        }
        ch[x][1] = u;
        if (u)
        {
            fa[u] = x; pfa[u] = 0;
            Change(FindRoot(u),rt,-1);
        }
    }
}
 
void ChangeRoot(int x) {Access(x); splay(x); rev[x] ^= 1;}
 
void Dfs(int x,int from)
{
    dfn[x] = ++dfs_clock; Name[dfs_clock] = x;
    for (int i = 1; i < N; i++) tfa[x][i] = tfa[tfa[x][i-1]][i-1];
    for (int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if (to == from) continue; pfa[to] = x;
        L[to] = L[x] + 1; tfa[to][0] = x; Dfs(to,x);
    }
    out[x] = dfs_clock;
}
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
int getcom()
{
    scanf("%s",s);
    if (s[2] == 'L') return 1;
    if (s[2] == 'C') return 2;
    return 3;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); m = getint();
    for (int i = 1; i < n; i++)
    {
        int x = getint(),y = getint();
        v[x].push_back(y); v[y].push_back(x);
    }
    L[1] = 1; Dfs(1,0); Build(1,1,n);
    while (m--)
    {
        int com = getcom(),now = getint();
        if (com == 1) Access(now),splay(now);
        else if (com == 2) ChangeRoot(now),rt = now;
        else
        {
            if (now == rt) printf("%.10lf\n",(DB)(Query(1,1,n,1,n)) / (DB)(n));
            else
            {
                int lca = LCA(rt,now);
                if (lca == now)
                {
                    int y = Quickfa(rt,L[rt] - L[now] - 1),tot;
                    LL Sum = 0; tot = dfn[y] - 1 + n - out[y];
                    if (dfn[y] > 1) Sum = Query(1,1,n,1,dfn[y] - 1);
                    if (out[y] < n) Sum += Query(1,1,n,out[y] + 1,n);
                    printf("%.10lf\n",(DB)(Sum) / (DB)(tot));
                }
                else
                {
                    int tot = out[now] - dfn[now] + 1;
                    LL Sum = Query(1,1,n,dfn[now],out[now]);
                    printf("%.10lf\n",(DB)(Sum) / (DB)(tot));
                }
            }
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值