zoukankan      html  css  js  c++  java
  • 【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询

    题目描述

    给出一棵n个节点的树,每一个节点开始有一个互不相同的颜色,初始根节点为1。
    定义一次感染为:将指定的一个节点到根的链上的所有节点染成一种新的颜色,代价为这条链上不同颜色的数目。
    现有m次操作,每次为一下三种之一:
    RELEASE x:对x执行一次感染;
    RECENTER x:把根节点改为x,并对原来的根节点执行一次感染;
    REQUEST x:询问x子树中所有节点感染代价的平均值。

    输入

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

    输出

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

    样例输入

    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

    样例输出

    4.0000000000
    2.0000000000
    1.3333333333


    题解

    LCT+树上倍增+DFS序+树状数组区间修改区间查询

    这也是一道神题了。。。不查题解真的想不到。。。

    首先同种颜色的一定是一条链。

    然后如果把同种颜色的边看作实边,不同种颜色的边看作虚边的话,

    一次感染就是LCT中的access操作,代价就是虚边数量+1。。。

    (估计出题人是根据access操作的方法才出的这题吧。。。正常人根本想不到是LCT啊。。。)

    一次RECENTER操作就是makeroot(makeroot自带一次access,就对应了题目的再感染一次。。。)

    所以一个点的答案只与虚边数量有关,而只有虚边变化(access)时某些点的答案才会变化。

    考虑加虚边答案怎么变化:一个点到其父亲节点加了一条虚边,那么其子树内所有节点到根节点都需要经过这一条虚边,该子树内答案+1。删虚边同理。

    这里要注意一个问题:Splay的根节点不是这条链上深度最小的点,最左边的节点才是要更新子树的节点。因此在更新子树时还要找Splay中最左边的节点,同时不要忘了pushdown。

    由于本题的根是变化的,因此参考 bzoj3083遥远的国度 ,将子树转化为dfs序上的至多两端区间,将得到的区间+1或-1。其中找第一个子节点的过程可以使用树上倍增实现。

    因为本体略卡常,所以采用了树状数组区间修改区间查询,方法参考 bzoj3132上帝造题的七分钟

    查询时同理,直接把区间和求出来即可。

    时间复杂度$O(nlog^2n*不知多大的常数)=O(能过)$

    #include <cstdio>
    #include <algorithm>
    #define N 100010
    using namespace std;
    typedef long long ll;
    struct lct
    {
    	int fa , c[2] , rev;
    }a[N];
    int n , head[N] , to[N << 1] , next[N << 1] , cnt , fa[N][20] , deep[N] , pos[N] , last[N] , tot , log[N] , root = 1;
    ll f[N] , g[N];
    char str[15];
    inline void modify(int x , int a)
    {
    	int i;
    	for(i = x ; i <= n ; i += i & -i) f[i] += a , g[i] += x * a;
    }
    inline ll query(int x)
    {
    	int i;
    	ll s1 = 0 , s2 = 0;
    	for(i = x ; i ; i -= i & -i) s1 += f[i] , s2 += g[i];
    	return (x + 1) * s1 - s2;
    }
    inline void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    void dfs(int x)
    {
    	int i;
    	pos[x] = ++tot;
    	for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa[x][0])
    			fa[to[i]][0] = x , a[to[i]].fa = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
    	last[x] = tot;
    	modify(pos[x] , 1) , modify(last[x] + 1 , -1);
    }
    inline int find(int x , int y)
    {
    	int i;
    	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
    		if((1 << i) < deep[x] - deep[y])
    			x = fa[x][i];
    	return x;
    }
    inline void rever(int x)
    {
    	swap(a[x].c[0] , a[x].c[1]) , a[x].rev ^= 1;
    }
    inline void pushdown(int x)
    {
    	if(a[x].rev) rever(a[x].c[0]) , rever(a[x].c[1]) , a[x].rev = 0;
    }
    inline bool isroot(int x)
    {
    	return a[a[x].fa].c[0] != x && a[a[x].fa].c[1] != x;
    }
    void update(int x)
    {
    	if(!isroot(x)) update(a[x].fa);
    	pushdown(x);
    }
    inline void rotate(int x)
    {
    	int y = a[x].fa , z = a[y].fa , l = (a[y].c[1] == x) , r = l ^ 1;
    	if(!isroot(y)) a[z].c[a[z].c[1] == y] = x;
    	a[x].fa = z , a[y].fa = x , a[a[x].c[r]].fa = y , a[y].c[l] = a[x].c[r] , a[x].c[r] = y;
    }
    inline void splay(int x)
    {
    	int y , z;
    	update(x);
    	while(!isroot(x))
    	{
    		y = a[x].fa , z = a[y].fa;
    		if(!isroot(y)) rotate((a[y].c[0] == x) ^ (a[z].c[0] == y) ? x : y);
    		rotate(x);
    	}
    }
    inline void treemodify(int x , int v)
    {
    	if(!x) return;
    	int y;
    	while(a[x].c[0]) pushdown(x) , x = a[x].c[0];
    	if(root == x) modify(1 , v);
    	else if(pos[root] < pos[x] || pos[root] > last[x]) modify(pos[x] , v) , modify(last[x] + 1 , -v);
    	else y = find(root , x) , modify(1 , v) , modify(pos[y] , -v) , modify(last[y] + 1 , v);
    }
    inline double treequery(int x)
    {
    	int y;
    	if(root == x) return (double)query(n) / n;
    	else if(pos[root] < pos[x] || pos[root] > last[x]) return (double)(query(last[x]) - query(pos[x] - 1)) / (last[x] - pos[x] + 1);
    	else
    	{
    		y = find(root , x);
    		return (double)(query(n) - query(last[y]) + query(pos[y] - 1)) / (n - last[y] + pos[y] - 1);
    	}
    }
    inline void access(int x)
    {
    	int t = 0 , y;
    	while(x) splay(x) , treemodify(t , -1) , y = a[x].c[1] , a[x].c[1] = t , treemodify(y , 1) , t = x , x = a[x].fa;
    }
    inline void makeroot(int x)
    {
    	access(x) , splay(x) , rever(x) , root = x;
    }
    int main()
    {
    	int m , i , x , y;
    	scanf("%d%d" , &n , &m);
    	for(i = 2 ; i <= n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x) , log[i] = log[i >> 1] + 1;
    	dfs(1);
    	while(m -- )
    	{
    		scanf("%s%d" , str , &x);
    		if(str[2] == 'L') access(x);
    		else if(str[2] == 'C') makeroot(x);
    		else printf("%.10lf
    " , treequery(x));
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    Silverlight 4常用StringFormat格式总结
    分享Silverlight/WPF/Windows Phone一周学习导读(4月18日4月23日)
    分享Silverlight/WPF/Windows Phone一周学习导读(05月16日05月21日)
    银光中国网免费Silverlight空间Web Service部署方法
    Silverlight实例教程 Navigation导航框架开篇
    分享Silverlight/WPF/Windows Phone一周学习导读(05月09日05月14日)
    分享Silverlight 3D开源项目和Silverlight/WPF/Windows Phone一周学习导读(4月25日4月29日)
    分享Silverlight/WPF/Windows Phone一周学习导读(06月06日06月11日)
    20个常用Expression Blend设计开发技巧 (1)
    Xen动态迁移中的内存热拷贝 [转]
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7603394.html
Copyright © 2011-2022 走看看