AGC002D Stamp Rally

传送门

题意(也不是我翻的有点长就将就着看嘛):
有一个N (3<=N<=105)个结点和M(N−1≤M≤105)个边的无向图。 结点编号为1到N,边编号为1到M。边i连接结点ai和bi。保证图连通。在这张图上,Q(1≤Q≤105)对兄弟正在参加一项名为Stamp Rally的活动。 第i对Stamp Rally如下:
一个兄弟从结点xi开始,另一个从结点yi开始。(1≤xi<yi≤N)
两个兄弟沿着边访问图上的结点,总共访问zi(3≤zi≤N)个结点,包括起始结点。 在这里,即使一个结点被多次访问,只计算一次。
定义得分为它们走过的边的最大编号。 他们的目标是尽量减少这个得分。
找出每对兄弟的最低分数。

题解:
突然想补一发很久以前写的题嘤嘤嘤
这道题要用到并查集和一个之前听都没听说过的整体二分。
整体二分,大概就是把询问和答案同时二分,每次判断中间值时把询问按照是否满足条件划分。然后并查集可以开logn层,在一个区间内判断完中点后把中点后面的也合并了,这样在回到上一层并到达右边区间时不必从1开始合并。

#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#include<algorithm>
#define maxn 100005
#define maxd 25
#define INF 0x3f3f3f3f
using namespace std;
int n,m,q,dist[maxn],fa[maxn][maxd],siz[maxn][maxd],u[maxn],v[maxn],ans[maxn];
bool vis[maxn];
struct node { int x,y,z,i; } qry[maxn],tmp[maxn];
int find(int x,int d)
{
	if(fa[x][d]==x) return x;
	return fa[x][d]=find(fa[x][d],d);
}
void BS(int l,int r,int ql,int qr,int d)
{
	if(l+1==r)
	{
		for(int i=ql;i<=qr;i++) ans[qry[i].i]=r;
		int r1=find(u[r],d),r2=find(v[r],d);
		if(r1!=r2) fa[r2][d]=r1,siz[r1][d]+=siz[r2][d];
		return;
	}
	int mid=(l+r)>>1;
	for(int i=l+1;i<=mid;i++)
	{
		int r1=find(u[i],d),r2=find(v[i],d);
		if(r1!=r2) fa[r2][d]=r1,siz[r1][d]+=siz[r2][d];
	}
	int tl=ql,tr=qr;
	for(int i=ql;i<=qr;i++)
	{
		int r1=find(qry[i].x,d),r2=find(qry[i].y,d);
		if((r1!=r2&&siz[r1][d]+siz[r2][d]>=qry[i].z)||(r1==r2&&siz[r1][d]>=qry[i].z)) tmp[tl++]=qry[i];
		else tmp[tr--]=qry[i];
	}
	for(int i=mid+1;i<=r;i++)
	{
		int r1=find(u[i],d),r2=find(v[i],d);
		if(r1!=r2) fa[r2][d]=r1,siz[r1][d]+=siz[r2][d];
	}
	for(int i=ql;i<=qr;i++) qry[i]=tmp[i];
	BS(l,mid,ql,tl-1,d+1); BS(mid,r,tl,qr,d+1);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=0;j<maxd;j++) fa[i][j]=i,siz[i][j]=1;
	for(int i=1;i<=m;i++) scanf("%d%d",&u[i],&v[i]);
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d%d",&qry[i].x,&qry[i].y,&qry[i].z);
		qry[i].i=i;
	}
	BS(0,m,1,q,0);
	for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值