歌名 - 歌手
0:00

    【bzoj4551】【NOIP2016模拟7.11】树

    题目

    在2016年,佳媛姐姐刚刚学习了树,非常开心。现在他想解决这样一个问题:给定一颗有根树(根为1),有以下
    两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均无标记,而且对于某个
    结点,可以打多次标记。)2. 询问操作:询问某个结点最近的一个打了标记的祖先(这个结点本身也算自己的祖
    先)你能帮帮他吗?

    分析

    此题有很多种方法

    暴力+并查集

    转了别人的
    考虑离线处理。
    我们将所有的操作倒序处理,利用并查集进行维护。对于一个修改操
    作(即失去标记),相当于将这一联通块块与它父亲的联通块合并。对于
    一个查询操作,直接查询所在的块的父亲即可。
    实现中有些细节需要注意,比如每个点可能会被标记多次,所以要以
    最早的被标记时间做为“失去标记”的时间。
    时间复杂度为O(n + qα(n))

    DFN序+线段树

    首先搞一遍dfn序,
    接着发现,在用一棵子树中,dfn序都是相邻的。
    好了,线段树修改查询。

    树链剖分

    单独修改、查询1到num的路径。
    裸题一枚。

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    const int maxlongint=2147483647;
    const int mo=1000000007;
    const int N=100005;
    using namespace std;
    int tree[N*6],top[N],d[N],size[N],deep[N],son[N],fa[N],n,m,tot,ans;
    int last[N*2],next[N*2],to[N*2],bef[N],aft[N];
    void bj(int x,int y)
    {
    	next[++tot]=last[x];
    	last[x]=tot;
    	to[tot]=y;
    }
    void dg(int x,int y)
    {
    	size[x]=1;
    	int mx=0;
    	for(int i=last[x];i;i=next[i])
    	{
    		int j=to[i];
    		if(j!=y)
    		{
    			deep[j]=deep[x]+1;
    			fa[j]=x;
    			dg(j,x);
    			size[x]+=size[j];
    			if(size[j]>mx)
    			{
    				mx=size[j];
    				son[x]=j;
    			}
    		}
    	}
    }
    int dg1(int x,int y)
    {
    	d[++tot]=x;
    	aft[x]=tot;
    	bef[tot]=x;
    	if(!top[x])
    		top[x]=x;
    	if(son[x])
    	{
    		top[son[x]]=top[x];
    		dg1(son[x],x);
    	}
    	for(int i=last[x];i;i=next[i])
    	{
    		int j=to[i];
    		if(j!=y && j!=son[x])
    		{
    			dg1(j,x);
    		}
    	}
    }
    int put(int v,int l,int r,int x)
    {
    	if(l==r)
    	{
    		tree[v]=bef[x];
    		return 0;
    	}
    	int mid=(l+r)/2;
    	if(x<=mid)
    	{
    		put(v*2,l,mid,x);
    	}
    	else
    		put(v*2+1,mid+1,r,x);
    	tree[v]=deep[tree[v*2]]>deep[tree[v*2+1]]?tree[v*2]:tree[v*2+1];
    }
    int find(int v,int l,int r,int x,int y)
    {
    	if(l==x && r==y)
    	{
    		ans=deep[ans]>deep[tree[v]]?ans:tree[v];
    		return 0;
    	}
    	int mid=(l+r)/2;
    	if(y<=mid)
    	{
    		find(v*2,l,mid,x,y);
    	}
    	else
    	if(mid<x)
    	{
    		find(v*2+1,mid+1,r,x,y);
    	}
    	else
    	{
    		find(v*2,l,mid,x,mid);
    		find(v*2+1,mid+1,r,mid+1,y);
    	}
    	tree[v]=deep[tree[v*2]]>deep[tree[v*2+1]]?tree[v*2]:tree[v*2+1];
    }
    int main()
    {
    	freopen("4604.in","r",stdin);
    	freopen("4604.out","w",stdout);
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n-1;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		bj(x,y);
    		bj(y,x);
    	}
    	deep[1]=1;
    	dg(1,0);
    	top[1]=1;
    	tot=0;
    	dg1(1,0);
    	put(1,1,n,aft[1]);
    	scanf("\n");
    	for(int i=1;i<=m;i++)
    	{
    		int c;
    		int x,y;
    		c=getchar();
    		while((c!='Q')&&(c!='C'))c=getchar();
    		scanf("%d\n",&x);
    		if(c=='C')
    		{
    			put(1,1,n,aft[x]);
    		}
    		else
    		{
    			ans=0;
    			while(top[x]!=top[1])
    			{
    				find(1,1,n,aft[top[x]],aft[x]);
    				x=fa[top[x]];
    			}
    			find(1,1,n,aft[1],aft[x]);
    			printf("%d\n",ans);
    		}
    	}
    }
    
    
    posted @ 2018-05-09 12:30  无尽的蓝黄  阅读(176)  评论(0编辑  收藏  举报