洛谷4197 BZOJ3545 BZOJ3551 peaks Kruskal重构树 主席树 线段树合并

44 篇文章 0 订阅
29 篇文章 0 订阅

题面链接

题意:
给你一个n个点m条边的无向图,边有边权,点有点权。有q次询问,每次问你从x出发,只能经过边权不超过y的边,能到达的所有点中点权第k大的权值是多少,如果不存在就输出-1。n<=1e5,m,q<=5e5
BZOJ3551和另外两个题号的题面的区别是多了一个强制在线。

题解:
先吐槽两句。写这个题真的是心态崩了。这个题本来并不怎么难,但是我却一直RE,过了对拍也没有用。在洛谷上提交了一页多的RE,又借同学的BZOJ权限号在BZOJ上交,还是RE。最后终于在一些奇奇怪怪的地方下了数据,然后发现,m是5e5???我一直看成了n,m<=1e5,q<=5e5,于是成功地一直RE。

下面说一下做法。
我写了两个写法,都是在线的。
我们先对边权求出一个Kruskal重构树,然后我们对于每次询问,还是在树上倍增找到最靠近根的合法的点,然后以这个点为根的子树内的所有点都是能到达的。那么我们就要找子树内的这些点权值第k大的了。由于点的权值是1e9级别的,所以我们先离散化一下。

这时候我有两种做法,第一个是用线段树合并,具体就是每次加进一个叶子就把它对应的离散化后的权值的位置+1,然后查询的话就找到这个点的这棵线段树,然后类似主席树找第k大的样子,判断左右子树的大小来决定递归到哪边。然后还要注意一下特判-1的情况。

另一个做法是用主席树维护dfs序。我们先处理出每个点在Kruskal重构树上的dfs序及子树结束时的dfs序。我们按照dfs序加入每一个点对应的权值,然后一个子树内的所有点对应的就是dfs序上一个区间,由于主席树是可以前缀相减的,于是减一下就能得到这个区间对应的主席树。于是剩下的就是主席树上找第k大了。

两种代码都放一下。

主席树的写法:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
using namespace std;

int n,m,q,bb[400010],c[400010],hed[400010],cnt,hed1[400010],cnt1;
int fa[400010],f[400010][21],xu[400010],num,num1,val[400010];
int ji[400010],ed[400010],root[400010];
struct node
{
	int from,to,next,c; 
}a[400010],b[800010];
struct tree
{
	int l,r,s;
}tr[4000010];
inline int read()
{
	int x=0,f=1;
	char s=getchar();
	while(s>'9'||s<'0')
	{
		if(s=='-')
		f=-1;
		s=getchar();
	}	
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x*f;
}
inline void add(int from,int to,int c)
{
	a[++cnt].to=to;
	a[cnt].from=from;
	a[cnt].next=hed[from];
	a[cnt].c=c;
	hed[from]=cnt;
}
inline int cmp(node x,node y)
{
	return x.c<y.c;
}
inline int getr(int x)
{ 
	if(x==fa[x])
	return x;
	else
	{
		fa[x]=getr(fa[x]);
		return fa[x];
	}
} 
inline void add1(int from,int to)
{
	b[++cnt1].to=to;
	b[cnt1].next=hed1[from];
	hed1[from]=cnt1;
}
inline void dfs(int x)
{
	xu[x]=++cnt;
	ji[cnt]=x;
	for(int i=1;i<=20;++i)
	f[x][i]=f[f[x][i-1]][i-1];
	for(int i=hed1[x];i;i=b[i].next)
	{
		int y=b[i].to;
		if(y==f[x][0])
		continue;
		f[y][0]=x;
		dfs(y);
	}
	ed[x]=cnt;
}
inline void update(int rt,int rt2,int l,int r,int x)
{
	tr[rt].s=tr[rt2].s;
	tr[rt].s++;
	if(l==r)
	return;	
	int mid=(l+r)>>1;
	if(x<=mid)
	{
		tr[rt].l=++cnt;
		tr[rt].r=tr[rt2].r;
		update(tr[rt].l,tr[rt2].l,l,mid,x);
	}
	else
	{
		tr[rt].r=++cnt;
		tr[rt].l=tr[rt2].l;
		update(tr[rt].r,tr[rt2].r,mid+1,r,x);
	}	
}
inline int lca(int x,int y)
{
	for(int i=20;i>=0;--i)
	{
		if(val[f[x][i]]<=y)
		x=f[x][i];		
	}
	return x;
}
inline int query(int rt1,int rt2,int l,int r,int k)
{
	if(l==r&&l==1&&k>tr[rt1].s-tr[rt2].s)
	return -1;
	if(l==r)
	return l;
	int s=tr[tr[rt1].r].s-tr[tr[rt2].r].s,mid=(l+r)>>1;
	if(s<k)
	return query(tr[rt1].l,tr[rt2].l,l,mid,k-s);
	else
	return query(tr[rt1].r,tr[rt2].r,mid+1,r,k);
}
int main()
{
	n=read();
	m=read();
	q=read();
	for(int i=1;i<=n;++i)
	{
		c[i]=read();
		bb[i]=c[i];
	}
	for(int i=1;i<=m;++i)
	{
		int x=read(),y=read(),z=read();
		add(x,y,z);
		add(y,x,z);
	}
	sort(bb+1,bb+n+1);
	num=unique(bb+1,bb+n+1)-bb-1;
	for(int i=1;i<=n;++i)
	c[i]=lower_bound(bb+1,bb+num+1,c[i])-bb;
	sort(a+1,a+cnt+1,cmp);
	for(int i=1;i<=2*n;++i)
	fa[i]=i; 
	for(int i=1;i<=n;++i)
	val[i]=0;
	num1=n;
	for(int i=1;i<=cnt;++i)
	{		
		int fx=getr(a[i].from),fy=getr(a[i].to);
		if(fx!=fy)
		{
			++num1;
			add1(fx,num1);
			add1(num1,fx);
			add1(num1,fy);
			add1(fy,num1);
			val[num1]=a[i].c;
			fa[fx]=fa[fy]=num1;
		}
	}
	val[0]=2e9;
	cnt=0;
	dfs(num1);
	cnt=0;
	root[0]=++cnt;
	for(int i=1;i<=num1;++i)
	{
		root[i]=++cnt;
		if(c[ji[i]]!=0)
		update(root[i],root[i-1],1,2*n,c[ji[i]]);
		else
		root[i]=root[i-1];
	}
	for(int i=1;i<=q;++i)
	{
		int x=read(),y=read(),z,k=read();
		z=lca(x,y);
		int gg;
		gg=query(root[ed[z]],root[xu[z]-1],1,2*n,k);
		if(gg==-1)
		printf("-1\n");
		else
		printf("%d\n",bb[gg]);
	}	
	return 0;
}

线段树合并写法:

#include <bits/stdc++.h>
using namespace std;

int n,m,q,hed[200010],cnt,hed1[200010],cnt1;
int bb[200010],c[200010],num,fa[200010],f[200010][21],num1;
int root[200010],val[200010];
struct node
{
	int from,to,l,next;
}a[1000010],b[2000010];
struct tree
{
	int l,r,s;
}tr[6000010];
inline int read()
{
	int x=0;
	char s=getchar();
	while(s>'9'||s<'0')
	s=getchar();
	while(s>='0'&&s<='9')
	{
		x=x*10+s-'0';
		s=getchar();
	}
	return x;
}
inline void add(int from,int to,int l)
{
	a[++cnt].from=from;
	a[cnt].to=to;
	a[cnt].l=l;
	a[cnt].next=hed[from];
	hed[from]=cnt;
}
inline int cmp(node x,node y)
{
	return x.l<y.l;
}
inline int getr(int x)
{
	if(x==fa[x])
	return fa[x];
	else
	{
		fa[x]=getr(fa[x]);
		return fa[x];
	}
}
inline void add1(int from,int to)
{
	b[++cnt1].to=to;
	b[cnt1].next=hed1[from];
	hed1[from]=cnt1;
}
inline void update(int &rt,int l,int r,int x)
{
	if(!rt)
	rt=++cnt;
	tr[rt].s++;
	if(l==r)
	return;
	int mid=(l+r)>>1;
	if(x<=mid)
	update(tr[rt].l,l,mid,x);
	else
	update(tr[rt].r,mid+1,r,x);
}
inline void merge(int &rt,int l,int r)
{
	if(!l||!r)
	{
		rt=l+r;
		return;
	}
	rt=++cnt;
	tr[rt].s=tr[l].s+tr[r].s;
	merge(tr[rt].l,tr[l].l,tr[r].l);
	merge(tr[rt].r,tr[l].r,tr[r].r);
}
inline void dfs(int x)
{
	for(int i=1;i<=20;++i)
	f[x][i]=f[f[x][i-1]][i-1];
	int pd=0;
	for(int i=hed1[x];i;i=b[i].next)
	{
		int y=b[i].to;
		if(y==f[x][0])
		continue;
		pd=1;
		f[y][0]=x;
		dfs(y);
		merge(root[x],root[x],root[y]);
	}
	if(pd==0)
	update(root[x],1,n,c[x]);
}
inline int lca(int x,int y)
{
	for(int i=20;i>=0;--i)
	{
		if(val[f[x][i]]<=y)
		x=f[x][i];
	} 
	return x;
}
inline int query(int rt,int l,int r,int k)
{
	if(l==r&&tr[rt].s<k)
	return -1;
	if(l==r)
	return bb[l];
	int mid=(l+r)>>1;
	if(tr[tr[rt].r].s>=k)
	return query(tr[rt].r,mid+1,r,k);
	else
	return query(tr[rt].l,l,mid,k-tr[tr[rt].r].s);
}
int main()
{
	n=read();
	m=read();
	q=read();	 
	for(int i=1;i<=n;++i)
	{
		c[i]=read();
		bb[i]=c[i];
	}	
	sort(bb+1,bb+n+1);
	num=unique(bb+1,bb+n+1)-bb-1;
	for(int i=1;i<=n;++i)
	c[i]=lower_bound(bb+1,bb+num+1,c[i])-bb;	
	for(int i=1;i<=m;++i)
	{		
		int x=read(),y=read(),z=read();
		add(x,y,z);
		add(y,x,z);
	}
	sort(a+1,a+cnt+1,cmp);
	for(int i=1;i<=2*n;++i)
	fa[i]=i;
	num1=n;	
	for(int i=1;i<=cnt;++i)
	{
		int fx=getr(a[i].from),fy=getr(a[i].to);
		if(fx!=fy)
		{
			++num1;
			add1(num1,fx);
			add1(fx,num1);
			add1(num1,fy);
			add1(fy,num1);
			val[num1]=a[i].l;
			fa[fx]=fa[fy]=num1;
		}
	}
	val[0]=2e9;
	cnt=0;
	dfs(num1);
	for(int i=1;i<=q;++i)
	{
		int x=read(),y=read(),k=read(),z;
		z=lca(x,y);
		int ji=query(root[z],1,n,k);
		printf("%d\n",ji);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值