zoukankan      html  css  js  c++  java
  • bzoj3779-重组病毒

    一颗(n)个点的树,初始颜色都不同,初始根为1。定义一个点到根的代价为这个点到根路径上不同颜色个数。有(m)个操作,分三种:

    • 将一个点到根路径上的所有点颜色改为一种新的颜色
    • 询问一个点的子树的所有点到根的代价和
    • 对点(x)进行操作1后把根换成(x)

    (n,mle 10^5)

    分析

    那个修改看着是不是很像access操作呢!换根操作之前要先access就是link-cut tree的makeroot呀!

    首先,令1的深度为1,那么初始所有点的代价就是他们的深度。我们用lct来维护这个树的结构,支持换根即可。所以一切的关键都集中在了access上。

    access其实就是实虚边切换的操作。注意到代价其实就是点到根路径上虚边个数+1,所以如果我们把一条实边改成虚边,那么整颗子树的答案加一,虚边改实边整个子树答案减一,用线段树维护一下dfs序就好啦。

    等等!我们不是换了根吗,怎么用dfs序维护子树呢?画一画图,设当前点为(x),发现:

    • (root=x),那么就是整个树
    • (root)(x)的子树中,那么就是整棵树除去(x)的root那颗子树
    • 否则就是原来的子树

    所以dfs序依然可以维护,只是要分情况讨论一下。

    代码

    #include<cstdio>
    #include<cctype>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long giant;
    int read() {
    	int x=0,f=1;
    	char c=getchar();
    	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*f;
    }
    const int maxn=1e5+10;
    const int maxj=18;
    int n,m,root=1,first[maxn],second[maxn],dft=0,f[maxn][maxj],dep[maxn];
    struct node {
    	int ch[2],fa;
    	bool rev;
    } t[maxn];
    struct SGT {
    	giant t[maxn<<2],tag[maxn<<2];
    	void push(int x,int L,int mid,int R) {
    		if (!tag[x]) return;
    		tag[x<<1]+=tag[x],tag[x<<1|1]+=tag[x];
    		t[x<<1]+=(mid-L+1)*tag[x];
    		t[x<<1|1]+=(R-mid)*tag[x];
    		tag[x]=0;
    	}
    	void add(int x,int L,int R,int l,int r,giant d) {
    		if (L==l && R==r) {
    			tag[x]+=d;
    			t[x]+=(R-L+1)*d;
    			return;
    		}
    		int mid=(L+R)>>1;
    		push(x,L,mid,R);
    		if (r<=mid) add(x<<1,L,mid,l,r,d); else 
    		if (l>mid) add(x<<1|1,mid+1,R,l,r,d); else
    		add(x<<1,L,mid,l,mid,d),add(x<<1|1,mid+1,R,mid+1,r,d);
    		t[x]=t[x<<1]+t[x<<1|1];
    	}
    	void add(int l,int r,giant d) {
    		if (l>r) return;
    		add(1,1,n,l,r,d);
    	}
    	giant query(int x,int L,int R,int l,int r) {
    		if (L==l && R==r) return t[x];
    		int mid=(L+R)>>1;
    		push(x,L,mid,R);
    		if (r<=mid) return query(x<<1,L,mid,l,r); 
    		if (l>mid) return query(x<<1|1,mid+1,R,l,r); else 
    		return query(x<<1,L,mid,l,mid)+query(x<<1|1,mid+1,R,mid+1,r);
    	}
    	giant query(int l,int r) {
    		if (l>r) return 0ll;
    		return query(1,1,n,l,r);
    	}
    } sgt;
    vector<int> g[maxn];
    void add(int x,int y) {g[x].push_back(y);}
    void dfs(int x,int fa) {
    	first[x]=++dft;
    	dep[x]=dep[fa]+1;
    	f[x][0]=fa;
    	sgt.add(first[x],first[x],dep[x]);
    	for (int v:g[x]) if (v!=fa) t[v].fa=x,dfs(v,x);
    	second[x]=dft;
    }
    int jump(int x,int y) {
    	if (!y) return x;
    	for (int j=0;j<maxj;++j) if ((y>>j)&1) x=f[x][j];
    	return x;
    }
    bool insub(int x,int y) {
    	return first[x]<=first[y] && first[y]<=second[x];
    }
    double query(int x) {
    	if (root==x) {
    		return (double)sgt.query(1,n)/n;
    	} else if (insub(x,root)) {
    		int p=jump(root,dep[root]-dep[x]-1);
    		double ret=sgt.query(1,first[p]-1)+sgt.query(second[p]+1,n);
    		ret/=(double)(first[p]-1+n-second[p]);
    		return ret;
    	} else {
    		return (double)sgt.query(first[x],second[x])/(second[x]-first[x]+1);
    	}
    }
    bool rson(int x) {
    	return t[t[x].fa].ch[1]==x;
    }
    bool isroot(int x) {
    	return !x || t[t[x].fa].ch[rson(x)]!=x;
    }
    void push(int x) {
    	if (t[x].rev) {
    		swap(t[x].ch[0],t[x].ch[1]);
    		if (t[x].ch[0]) t[t[x].ch[0]].rev^=true;
    		if (t[x].ch[1]) t[t[x].ch[1]].rev^=true;
    		t[x].rev=false;
    	}
    }
    void down(int x) {
    	if (!isroot(x)) down(t[x].fa);
    	push(x);
    }
    void rotate(int x) {
    	int f=t[x].fa,d=rson(x),c=t[x].ch[d^1];
    	if (!isroot(f)) t[t[f].fa].ch[rson(f)]=x;
    	if (c) t[c].fa=f;
    	t[x].fa=t[f].fa,t[f].fa=x,t[f].ch[d]=c,t[x].ch[d^1]=f;
    }
    void splay(int x) {
    	down(x);
    	while (!isroot(x)) {
    		if (isroot(t[x].fa)) rotate(x); else {
    			if (rson(x)==rson(t[x].fa)) rotate(t[x].fa),rotate(x); else 
    			rotate(x),rotate(x);
    		}
    	}
    }
    void change(int x,giant d) {
    	if (x==root) sgt.add(1,n,d); else 
    	if (insub(x,root)) {
    		int p=jump(root,dep[root]-dep[x]-1);
    		sgt.add(1,first[p]-1,d);
    		sgt.add(second[p]+1,n,d);
    	} else {
    		sgt.add(first[x],second[x],d);
    	}
    }
    int left(int x) {
    	for (push(x);t[x].ch[0];x=t[x].ch[0],push(x));
    	return x;
    }
    void ace(int x) {
    	for (int last=0;x;x=t[last=x].fa) {
    		splay(x);
    		giant tmp=sgt.query(first[x],first[x]);
    		if (t[x].ch[1]) change(left(t[x].ch[1]),1);
    		if (last) change(left(last),-1);
    		t[x].ch[1]=last;
    	}
    }
    void cent(int x) {
    	root=x;
    	splay(x);
    	t[x].rev^=true;
    }
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    	freopen("my.out","w",stdout);
    #endif
    	n=read(),m=read();
    	for (int i=1;i<n;++i) {
    		int x=read(),y=read();
    		add(x,y),add(y,x);
    	}
    	dfs(1,1);
    	for (int j=1;j<maxj;++j) for (int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];
    	while (m--) {
    		static char ord[20];
    		scanf("%s",ord);
    		int x=read();
    		if (ord[2]=='Q') {
    			double ans=query(x);
    			printf("%.10lf
    ",ans);
    		} else {
    			ace(x);
    			if (ord[2]=='C') cent(x);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    记一个centos分区大小调整过程
    破解StarUML3.01最新版 for Linux(Ubuntu16LTS)
    为什么我们要使用int类型来保存时间类型的数据。
    sphinx-doc的中文搜索
    ubuntu下file_get_contents返回空字符串
    PSR-PHP开发规范(本文版权归作者:luluyrt@163.com)
    PHP单例模式
    PHP中 PCRE正则表达式模式修饰符“u” 的使用。
    Mysql 插入时间时报错Incorrect datetime value: '' for column 'createtime'
    如何给list清空
  • 原文地址:https://www.cnblogs.com/owenyu/p/6912377.html
Copyright © 2011-2022 走看看