zoukankan      html  css  js  c++  java
  • 【XSY2534】【BZOJ4817】树点涂色 LCT 倍增 线段树 dfs序

    题目大意

    ​  Bob有一棵(n)个点的有根树,其中(1)号点是根节点。Bob在每个点上涂了颜色,并且每个点上的颜色不同。定义一条路径的权值是:这条路径上的点(包括起点和终点)共有多少种不同的颜色。Bob可能会进行这几种操作:

    ​  (1~x):把点(x)到根节点的路径上所有的点染上一种没有用过的新颜色。

    ​  (2~x~y):求(x)(y)的路径的权值。

    ​  (3~x~y):在以(x)为根的子树中选择一个点,使得这个点到根节点的路径权值最大,求最大权值。

    ​  Bob一共会进行(m)次操作

      (n,mleq 100000)

    题解

    ​  注意到操作(1)就是(access)操作,可以用LCT维护。

    ​  设(f_i=)(i)到根的路径权值,则操作(2)的答案为(f_x+f_y-2f_{lca}+1)

    ​  如果只有操作(1)和操作(2)的话,就可以暴力往上跳,但是操作(3)就没那么好处理了。

    ​  所以我们可以用线段树+dfs序维护(f_i)

    ​  (access)切换虚实边时一棵子树的(f)全部(+1),另一棵子树的(f)全部(-1)

    ​  然后用倍增求LCA

      时间复杂度:(O(nlog^2n))

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<ctime>
    #include<utility>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> pii;
    int n,m;
    namespace seg
    {
    	int s[4000010];
    	int t[4000010];
    	int n;
    	void init()
    	{
    		n=0;
    		memset(s,0,sizeof s);
    		memset(t,0,sizeof t);
    	}
    	void at(int x,int v)
    	{
    		s[x]+=v;
    		t[x]+=v;
    	}
    	void push(int x)
    	{
    		at(x*2,t[x]);
    		at(x*2+1,t[x]);
    		t[x]=0;
    	}
    	void add(int p,int l,int r,int v,int L,int R)
    	{
    		if(l<=L&&r>=R)
    		{
    			at(p,v);
    			return;
    		}
    		if(t[p])
    			push(p);
    		int mid=(L+R)>>1;
    		if(l<=mid)
    			add(p*2,l,r,v,L,mid);
    		if(r>mid)
    			add(p*2+1,l,r,v,mid+1,R);
    		s[p]=max(s[p*2],s[p*2+1]);
    	}
    	int query(int p,int l,int r,int L,int R)
    	{
    		if(l<=L&&r>=R)
    			return s[p];
    		if(t[p])
    			push(p);
    		int mid=(L+R)>>1;
    		int res=0;
    		if(l<=mid)
    			res=max(res,query(p*2,l,r,L,mid));
    		if(r>mid)
    			res=max(res,query(p*2+1,l,r,mid+1,R));
    		return res;
    	}
    }
    namespace tree
    {
    	struct list
    	{
    		int v[200010];
    		int t[200010];
    		int h[100010];
    		int n;
    		list()
    		{
    			n=0;
    			memset(h,0,sizeof h);
    		}
    		void add(int x,int y)
    		{
    			n++;
    			v[n]=y;
    			t[n]=h[x];
    			h[x]=n;
    		}
    	};
    	list l;
    	int bg[100010];
    	int ed[100010];
    	int ti;
    	int f[100010][20];
    	int d[100010];
    	void init()
    	{
    		memset(f,0,sizeof f);
    		ti=0;
    	}
    	void dfs(int x,int fa,int dep)
    	{
    		bg[x]=++ti;
    		f[x][0]=fa;
    		d[x]=dep;
    		int i;
    		for(i=1;i<=19;i++)
    			f[x][i]=f[f[x][i-1]][i-1];
    		for(i=l.h[x];i;i=l.t[i])
    			if(l.v[i]!=fa)
    				dfs(l.v[i],x,dep+1);
    		ed[x]=ti;
    	}
    	int getlca(int x,int y)
    	{
    		if(d[x]<d[y])
    			swap(x,y);
    		int i;
    		for(i=19;i>=0;i--)
    			if(d[f[x][i]]>=d[y])
    				x=f[x][i];
    		if(x==y)
    			return x;
    		for(i=19;i>=0;i--)
    			if(f[x][i]!=f[y][i])
    			{
    				x=f[x][i];
    				y=f[y][i];
    			}
    		return f[x][0];
    	}
    }
    namespace lct
    {
    	int f[100010];
    	int a[100010][2];
    	void init()
    	{
    		memset(f,0,sizeof f);
    		memset(a,0,sizeof a);
    	}
    	int root(int x)
    	{
    		return a[f[x]][0]!=x&&a[f[x]][1]!=x;
    	}
    	void rotate(int x)
    	{
    		if(root(x))
    			return;
    		int p=f[x];
    		int q=f[p];
    		int ps=(x==a[p][1]);
    		int qs=(p==a[q][1]);
    		int ch=a[x][ps^1];
    		if(!root(p))
    			a[q][qs]=x;
    		a[x][ps^1]=p;
    		a[p][ps]=ch;
    		if(ch)
    			f[ch]=p;
    		f[p]=x;
    		f[x]=q;
    	}
    	int splay(int x)
    	{
    		while(!root(x))
    		{
    			int p=f[x];
    			if(!root(p))
    				if((x==a[p][1])^(p==a[f[p]][1]))
    					rotate(p);
    				else
    					rotate(x);
    			rotate(x);
    		}
    	}
    	void access(int x)
    	{
    		int t=0;
    		int y=x,z;
    		while(x)
    		{
    			splay(x);
    			if(t)
    			{
    				while(a[t][0])
    					t=a[t][0];
    				splay(t);
    				seg::add(1,tree::bg[t],tree::ed[t],-1,1,n);
    			}
    			z=a[x][1];
    			a[x][1]=t;
    			if(z)
    			{
    				while(a[z][0])
    					z=a[z][0];
    				splay(z);
    				seg::add(1,tree::bg[z],tree::ed[z],1,1,n);
    			}
    			t=x;
    			x=f[x];
    		}
    		splay(y);
    	}
    }
    int main()
    {
    	lct::init();
    	seg::init();
    	tree::init();
    //	freopen("bzoj4817.in","r",stdin);
    //	freopen("bzoj4817.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	int i,op,x,y,ans;
    	for(i=1;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		tree::l.add(x,y);
    		tree::l.add(y,x);
    	}
    	tree::dfs(1,0,1);
    	for(i=2;i<=n;i++)
    		lct::f[i]=tree::f[i][0];
    	for(i=1;i<=n;i++)
    		seg::add(1,tree::bg[i],tree::bg[i],tree::d[i],1,n);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d",&op);
    		if(op==1)
    		{
    			scanf("%d",&x);
    			lct::access(x);
    		}
    		else if(op==2)
    		{
    			scanf("%d%d",&x,&y);
    			int lca=tree::getlca(x,y);
    			ans=seg::query(1,tree::bg[x],tree::bg[x],1,n)+
    					seg::query(1,tree::bg[y],tree::bg[y],1,n)-
    					2*seg::query(1,tree::bg[lca],tree::bg[lca],1,n)+1;
    			printf("%d
    ",ans);
    		}
    		else
    		{
    			scanf("%d",&x);
    			ans=seg::query(1,tree::bg[x],tree::ed[x],1,n);
    			printf("%d
    ",ans);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    sql server 中 bit 字段的 查询方法
    C# 4.0新特性"协变"与"逆变"以及背后的编程思想
    marginleft是做边距,是宽度 left是定位盒子左上角左边位置的一个点
    什么是企业库 怎么使用企业库 企业库的好处 企业库的目标
    SQL各种Join用法(Full,Left,Out,Inner)
    JavaScript计算两个文本框内数据的乘积(四舍五入保留两位小数)
    SQL VIEW(视图)
    Sql ISNULL() 函数
    left join 和 left outer join 有什么区别
    18个不常见的C#关键字,您使用过几个?
  • 原文地址:https://www.cnblogs.com/ywwyww/p/8509962.html
Copyright © 2011-2022 走看看