BZOJ 3551 ONTAK2010 Peaks加强版 Kruskal重构树+可持久化线段树

题目大意:同3545 强制在线

3545题解传送门:http://blog.csdn.net/popoqqq/article/details/40660953

强制在线没法排序 启发式合并也就用不了了

Kruskal重构树是个挺好玩的东西 可以拿来处理一些最小生成树的边权最值问题

这里我们Kruskal连边时并不直接连边 而是新建一个节点ext 将两个点所在子树都连到ext的儿子上

比如说样例的树就建成了这样


图中红色的是原图的边权,黑色的是原图上的点

这样生成的树有一些十分优美的性质:

1.二叉树(好吧这题意义不大)

2.原树与新树两点间路径上边权(点权)的最大值相等

3.子节点的边权小于等于父亲节点(大根堆)

4.原树中两点之间路径上边权的最大值等于新树上两点的LCA的点权

于是对于每个询问 我们从这个询问向上倍增寻找深度最小的点权小于等于x的点 易证这个节点的子树就是v所能到达的所有点

DFS序+可持久化线段树直接搞就行

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200200
using namespace std;
struct Segtree{
	Segtree *ls,*rs;
	int num;
	void* operator new (size_t size,Segtree *_,Segtree *__,int ___);
}*tree[M],*mempool,*C;
struct edge{
	int x,y,f;
	bool operator < (const edge &Y) const
	{
		return f < Y.f ;
	}
}edges[500500];
struct abcd{
	int to,next;
}table[M];
int head[M],tot;
int n,m,q,ans;
int a[M],fa[M][20],dis[M][20],belong[M];
int seq[M],l[M],r[M],cnt;
void* Segtree :: operator new (size_t size,Segtree *_,Segtree *__,int ___)
{
	if(C==mempool)
	{
		C=new Segtree[1<<15];
		mempool=C+(1<<15);
	}
	C->ls=_;
	C->rs=__;
	C->num=___;
	return C++;
}
void Add(int x,int y)
{
	table[++tot].to=y;
	table[tot].next=head[x];
	head[x]=tot;
}
int Find(int x)
{
	if(!belong[x]||belong[x]==x)
		return belong[x]=x;
	return belong[x]=Find(belong[x]);
}
void Kruskal()
{
	int i;
	sort(edges+1,edges+m+1);
	for(i=1;i<=m;i++)
	{
		int x=Find(edges[i].x);
		int y=Find(edges[i].y);
		if(x==y) continue;
		belong[x]=belong[y]=++n;
		fa[x][0]=fa[y][0]=n;
		dis[x][0]=dis[y][0]=edges[i].f;
		Add(n,x);Add(n,y);
	}
}
void DFS(int x)
{
	int i;
	seq[l[x]=++cnt]=a[x];
	for(i=head[x];i;i=table[i].next)
		DFS(table[i].to);
	r[x]=cnt;
}
Segtree* Build_Tree(Segtree *p,int x,int y,int val)
{
	int mid=x+y>>1;
	if(x==y) return new (0x0,0x0,p->num+1) Segtree;
	if(val<=mid) return new (Build_Tree(p->ls,x,mid,val),p->rs,p->num+1) Segtree;
	else return new (p->ls,Build_Tree(p->rs,mid+1,y,val),p->num+1) Segtree;
}
int Get_Kth(Segtree *p1,Segtree *p2,int x,int y,int k)
{
	int mid=x+y>>1;
	if(x==y) return mid;
	int temp=p2->rs->num-p1->rs->num;
	if(k<=temp) return Get_Kth(p1->rs,p2->rs,mid+1,y,k);
	else return Get_Kth(p1->ls,p2->ls,x,mid,k-temp);
}
int Get_Root(int x,int y)
{
	int j;
	for(j=19;~j;j--)
		if( fa[x][j] && dis[x][j]<=y )
			x=fa[x][j];
	return x;
}
int main()
{
	int i,j,v,x,k;
	cin>>n>>m>>q;
	for(i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(i=1;i<=m;i++)
		scanf("%d%d%d",&edges[i].x,&edges[i].y,&edges[i].f);
	Kruskal();
	for(j=1;j<=19;j++)
		for(i=1;i<=n;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1],
			dis[i][j]=max(dis[i][j-1],dis[fa[i][j-1]][j-1]);
	DFS(n);
	tree[0]=new (0x0,0x0,0) Segtree;
	tree[0]->ls=tree[0]->rs=tree[0];
	for(i=1;i<=n;i++)
		tree[i]=Build_Tree(tree[i-1],0,1000000000,seq[i]);
	for(i=1;i<=q;i++)
	{
		scanf("%d%d%d",&v,&x,&k);
		x^=ans;v^=ans;k^=ans;
		int root=Get_Root(v,x);
		if(r[root]-l[root]+1<k)
		{
			puts("-1");
			ans=0;
			continue;
		}
		ans=Get_Kth(tree[l[root]-1],tree[r[root]],0,1000000000,k);
		if(!ans)
		{
			puts("-1");
			continue;
		}
		printf("%d\n",ans);
	}
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值