• [SDOI2013]森林(启发式合并)(主席树)


    题目描述
    小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有M条边。

    小Z希望执行T个操作,操作有两类:

    Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。
    L x y在点x和点y之间连接一条边。保证完成此操作后,仍然是一片森林。
    为了体现程序的在线性,我们把输入数据进行了加密。设lastans为程序上一次输出的结果,初始的时候lastans为0。

    对于一个输入的操作Q x y k,其真实操作为Q x^lastans y^lastans k^lastans。
    对于一个输入的操作L x y,其真实操作为L x^lastans ylastans。其中运算符表示异或,等价于pascal中的xor运算符。
    请写一个程序來帮助小Z完成这些操作。

    对于所有的数据,n,m,T<=8*10^48∗10
    4

    输入格式
    第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1<=testcase<=20。

    第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。

    第三行包含N个非负整数表示 N个节点上的权值。

    接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边。

    接下来 T行,每行描述一个操作,格式为”Q x y k“或者”L x y “,其含义见题目描述部分。

    题目要求的是实现两个操作:查询区间k小值和连边操作。

    查询:主席树

    连边:LCT

    主席树+LCT

    首先码量就有一定压力、程序的实现性,但是也不是不能做。

    那有没有一些更容易实现的方法呢?

    看起来好像区间k小值应该是主席树跑不掉了,那连边操作呢?

    好像可以合并主席树。

    但又好像直接时间不太行,那怎么办?

    答:主席树+LCT

    其实可以启发式合并,对每一棵原树记录它的size,每一次合并的时候就相当于将一棵树合并到另一棵树上,启发式合并,将size小的合并到size大的上面去,每次合并的时候用并查集记录,修改时直接找到并查集顶端的元素即可。

    题目要求的是树上的区间k小,所以要对于树建主席树,其实就是儿子在爸爸的主席树的基础上添加,跟普通的主席树没有太大的区别,仅仅需要dfs建树罢了。

    建树:

    void insert(int &hao,int last,int l,int r,int x)//插入点
    {
    	hao=++cnt;
    	tr[hao]=tr[last];
    	tr[hao].size++;
    	if(l==r)
    	{
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid)
    	{
    		insert(tr[hao].lson,tr[last].lson,l,mid,x);
    	}else{
    		insert(tr[hao].rson,tr[last].rson,mid+1,r,x);
    	}
    }
    void dfs(int u,int root,int father)//建树(在爸爸的基础上)
    {
    	fa[u][0]=father;//这里更新lca,查询要用
    	for(int i=1;i<=17;i++)
    	{
    		fa[u][i]=fa[fa[u][i-1]][i-1];
    	}
    	vis[u]=1;
    	size[root]++;
    	deep[u]=deep[father]+1;
    	fas[u]=father;
    	insert(rt[u],rt[father],1,cnt1,lower_bound(b+1,b+cnt1+1,a[u])-b);
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v!=father)
    		{
    			dfs(v,root,u);
    		}
    	}
    }
    

    查询时就是像普通主席树一样。

    在这里插入图片描述

    题目要求的是一条路径,所以需要四个地方的size:lca的父亲,lca,x,y

    像图中这样x到根的路径,y到根的路径减去lca到根的路径,lca的父亲到根的路径,就得到了要求的路径。

    这条路径上的点数就是(x.size+y.size-lca.size-lca的父亲.size),同理,左边的主席树的size也可以求的,从而确定是往左找还是往右找。

    查询:

    int query(int hao,int hao1,int lca,int falca,int l,int r,int k)
    {
    	if(l==r)
    	{
    		return b[l];
    	}
    	int mid=(l+r)/2
    	int midsize=tr[tr[hao].lson].size+tr[tr[hao1].lson].size-tr[tr[lca].lson].size-tr[tr[falca].lson].size;//主席树左边的size
    	if(midsize>=k)//往左找或往右找
    	{
    		return query(tr[hao].lson,tr[hao1].lson,tr[lca].lson,tr[falca].lson,l,mid,k);
    	}else{
    		return query(tr[hao].rson,tr[hao1].rson,tr[lca].rson,tr[falca].rson,mid+1,r,k-midsize);
    	}
    }
    

    程序:

    #pragma GCC optimize(3)
    #pragma GCC optimize(2)
    #include<bits/stdc++.h>
    #define N 80010
    using namespace std;
    struct data
    {
    	int size,lson,rson;
    }tr[N*500];
    int to[N<<2],nxt[N<<2],nct,head[N],fas[N],fa[N][18],cnt,rt[N],cnt1,a[N],b[N],size[N],deep[N],n,m,k,x,y,t,lastans;
    char s[3];
    bool vis[N];
    void adde(int x,int y)
    {
    	to[++nct]=y;
    	nxt[nct]=head[x];
    	head[x]=nct;
    }
    int get_fa(int x)//并查集找爸爸
    {
    	if(fas[x]==x)
    	{
    		return x;
    	}
    	return fas[x]=get_fa(fas[x]);
    }
    void build(int &hao,int l,int r)//建一棵空的树,便于操作
    {
    	hao=++cnt;
    	tr[hao].size=0;
    	if(l==r)
    	{
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(tr[hao].lson,l,mid);
    	build(tr[hao].rson,mid+1,r);
    }
    void insert(int &hao,int last,int l,int r,int x)//插入点
    {
    	hao=++cnt;
    	tr[hao]=tr[last];
    	tr[hao].size++;
    	if(l==r)
    	{
    		return;
    	}
    	int mid=(l+r)>>1;
    	if(x<=mid)
    	{
    		insert(tr[hao].lson,tr[last].lson,l,mid,x);
    	}else{
    		insert(tr[hao].rson,tr[last].rson,mid+1,r,x);
    	}
    }
    void dfs(int u,int root,int father)//建树(在爸爸的基础上)
    {
    	fa[u][0]=father;//这里更新lca,查询要用
    	for(int i=1;i<=17;i++)
    	{
    		fa[u][i]=fa[fa[u][i-1]][i-1];
    	}
    	vis[u]=1;
    	size[root]++;
    	deep[u]=deep[father]+1;
    	fas[u]=father;
    	insert(rt[u],rt[father],1,cnt1,lower_bound(b+1,b+cnt1+1,a[u])-b);
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(v!=father)
    		{
    			dfs(v,root,u);
    		}
    	}
    }
    int get_lca(int x,int y)
    {
    	if(deep[x]<deep[y])
    	{
    		swap(x,y);
    	}
    	for(int i=17;i>=0;i--)
    	{
    		if(deep[fa[x][i]]>=deep[y])
    		{
    			x=fa[x][i];
    		}
    	}
    	if(x==y)
    	{
    		return x;
    	}
    	for(int i=17;i>=0;i--)
    	{
    		if(fa[x][i]!=fa[y][i])
    		{
    			x=fa[x][i];
    			y=fa[y][i];
    		}
    	}
    	return fa[x][0];
    }
    int query(int hao,int hao1,int lca,int falca,int l,int r,int k)
    {
    	if(l==r)
    	{
    		return b[l];
    	}
    	int mid=(l+r)/2,midsize=tr[tr[hao].lson].size+tr[tr[hao1].lson].size-tr[tr[lca].lson].size-tr[tr[falca].lson].size;//主席树左边的size
    	if(midsize>=k)//往左找或往右找
    	{
    		return query(tr[hao].lson,tr[hao1].lson,tr[lca].lson,tr[falca].lson,l,mid,k);
    	}else{
    		return query(tr[hao].rson,tr[hao1].rson,tr[lca].rson,tr[falca].rson,mid+1,r,k-midsize);
    	}
    }
    int main()
    {
    //	freopen("1.txt","r",stdin);
    	scanf("%d",&n);
    	scanf("%d%d%d",&n,&m,&t);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		b[i]=a[i];
    		fas[i]=i;
    	}
    	sort(b+1,b+n+1);//离散化
    	cnt1=unique(b+1,b+n+1)-b-1;
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		adde(x,y);
    		adde(y,x);
    	}
    	build(rt[0],1,cnt1);
    	for(int i=1;i<=n;i++)
    	{
    		if(!vis[i])
    		{
    			dfs(i,i,0);
    			fas[i]=i;//重新确定并查集顶端元素,不然会TLE
    		}
    	}
    	while(t--)//操作
    	{
    		scanf("%s%d%d",s,&x,&y);
    		x^=lastans;
    		y^=lastans;
    		if(s[0]=='Q')
    		{
    			scanf("%d",&k);
    			k^=lastans;
    			int lca=get_lca(x,y);
    			printf("%d
    ",lastans=query(rt[x],rt[y],rt[lca],rt[fa[lca][0]],1,cnt1,k));
    		}else{
    			adde(x,y);
    			adde(y,x);
    			int xx=get_fa(x),yy=get_fa(y);
    			if(size[xx]<size[yy])
    			{
    				swap(xx,yy);
    				swap(x,y);
    			}
    			dfs(y,xx,x);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    JPA 系列教程1-环境搭建
    微信企业号接收消息(使用SpringMVC)
    oracle xe 数据库用户操作
    eclipse快捷键
    堆和栈的区别(重要)
    synchronized的4种用法
    servlet匹配规则和顺序
    JAVA中的枚举
    JSON对象操作
    Handler
  • 原文地址:https://www.cnblogs.com/2017gdgzoi44/p/12111006.html
Copyright © 2020-2023  润新知