【可持久化并查集】P4768 [NOI2018]归程

5 篇文章 0 订阅
2 篇文章 0 订阅

首先,由于题目强制在线,所以需要用到可持久化并查集去维护动态的并查集,去动态的记录fa数组。

跑最短路要dijkstra

对于高度要离散化,否则点太多

 

 

 

细节特别多。。。。

 

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=4e5+5;
int t,n,q,cnt,k,s,m,tot,dep[maxn<<5],root[maxn],head[maxn],dis[maxn],vis[maxn],b[maxn<<1];
struct edge
{
	int to,nxt,len;
}G[maxn<<1];
struct eedge
{
	int from,to,hei;
	bool operator < (eedge other) const
	{
		return hei<other.hei;
	}
}e[maxn<<1];
struct node
{
	int u,d;
	bool operator < (node other) const
	{
		return d>other.d;
	}
};
priority_queue <node> que;
void add(int x,int y,int z)
{
	G[++tot].nxt=head[x];
	G[tot].to=y; 
	G[tot].len=z;
	head[x]=tot;
}
void dijkstra()
{
	memset(dis,0x3f,(n+1)<<2);
	memset(vis,0,(n+1)<<2);
	que.push((node){1,0});
	dis[1]=0;
	while(!que.empty())
	{
		node cur=que.top(); que.pop();
		if(vis[cur.u]) continue;
		vis[cur.u]=1;
		for(int i=head[cur.u];i;i=G[i].nxt)
			if(dis[G[i].to]>dis[cur.u]+G[i].len)
				que.push((node){G[i].to,dis[G[i].to]=dis[cur.u]+G[i].len});
	}
}
int lc[maxn<<5],rc[maxn<<5],mn[maxn<<5],f[maxn<<5];
void build(int &u,int l,int r)
{
	u=++cnt;
	if(l==r)
	{
		mn[u]=dis[f[u]=l];
		return;
	}
	int mid=(l+r)>>1;
	build(lc[u],l,mid);
	build(rc[u],mid+1,r);
}
int getf(int rt,int t)
{
	int u,l,r;
	while(1)
	{
		u=rt,l=1,r=n;
		while(l!=r)
		{
			int mid=(l+r)>>1;
			if(t<=mid) r=mid,u=lc[u];
			else l=mid+1,u=rc[u];
		}
		if(t==f[u]) break;
		t=f[u];
	}
	return u;
}
int insert(int *u,int v,int t)
{
	int l=1,r=n;
	while(l!=r)
	{
		*u=++cnt;
		int m=(l+r)>>1;
		if(t<=m)r=m,rc[*u]=rc[v],u=lc+*u,v=lc[v];
		else  l=m+1,lc[*u]=lc[v],u=rc+*u,v=rc[v];
	}
	return *u=++cnt;
}
int main()
{
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	int t;  scanf("%d",&t);
	while(t--)
	{
		cnt=tot=0;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
		{
			int u,v,z,zz;scanf("%d%d%d%d",&u,&v,&z,&zz);
			add(u,v,z); add(v,u,z);
			e[i]=((eedge){u,v,zz});
		}
		dijkstra();
		sort(e+1,e+m+1);
		for(int i=1;i<=m;i++) b[i]=e[i].hei;
		scanf("%d%d%d",&q,&k,&s);
		b[m+1]=s+1; int L=unique(b+1,b+m+2)-b-1;
		build(root[L],1,n);
		int j,i;
		for(i=L-1,j=m;i;i--)
		{
			root[i]=root[i+1];
			for(;j&&e[j].hei==b[i];j--)
			{
				int fu=getf(root[i],e[j].from),fv=getf(root[i],e[j].to);
				if(fu==fv) continue;
				if(dep[fu]>dep[fv]) swap(fu,fv);
				f[insert(&root[i],root[i],f[fu])]=f[fv];
				int tmp=insert(&root[i],root[i],f[fv]);
				f[tmp]=f[fv];
				mn[tmp]=min(mn[fu],mn[fv]);
				dep[tmp]=dep[fv]+(dep[fu]==dep[fv]);
			}
		}
		int lans=0;
		while(q--)
		{
			int v,u; scanf("%d%d",&v,&u);
			v=(v+k*lans-1)%n+1;
			u=(u+k*lans)%(s+1);
			printf("%d\n",lans=mn[getf(root[upper_bound(b+1,b+L+1,u)-b],v)]);
		}
		for(int i=1;i<=cnt;i++)
			f[i]=lc[i]=rc[i]=mn[i]=dep[i]=0;
		for(int i=1;i<=n;i++) head[i]=0;
		memset(root,0,sizeof(root));
	}
	return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值