[NOI 2018]归程

题目描述:

n个点,m条边的无向图,每个边有权值和高度。
每次询问给出出发点和最低高度,当边的高度高于最低高度时才可以通行,问从出发点可以到达离点1最近的点距离是多少

题目分析:

由于是无向图,先对点1跑单源最短路。
对图做克鲁斯卡尔重构树,由于是高于某个值才能通行,对边权降序排列,做小根堆,对出发的点走到最低不能走的位置,对子树内询问最小的距离即可

题目链接:

NOI2018 归途

AC代码:

#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
const int maxm=420000,INF=2147483646;
struct node{
	int u,v,w;
}edge[maxm];
struct Node{
	int dis,id;
	bool operator <(const Node &a)const
	{
		return a.dis<dis;
	}
};
int head[maxm],to[maxm*2],net[maxm*2],cost[maxm*2];
int dis[maxm],vis[maxm];
int fa[maxm],fat[maxm][20],val[maxm][20];
int cnt,n,m,sz; 
std::priority_queue<Node> dl;
inline void addedge(int u,int v,int c){to[++cnt]=v,cost[cnt]=c,net[cnt]=head[u],head[u]=cnt;}
inline bool comp(node x,node y){return x.w>y.w;}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
inline void dijskra(int st)
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	dis[st]=0;
	dl.push((Node){0,st});
	while(!dl.empty())
	{
		Node now=dl.top();
		dl.pop();
		if(vis[now.id]) continue;
		vis[now.id]=1;
		for(int i=head[now.id];i;i=net[i])
		if(!vis[to[i]]&&dis[to[i]]>dis[now.id]+cost[i])
		{
			dis[to[i]]=dis[now.id]+cost[i];
			dl.push(Node{dis[to[i]],to[i]});
		}
	}
}
inline void kruskal()
{
	sz=n;
	for(int i=1;i<=n;i++) fa[i]=i;
	std::sort(edge+1,edge+m+1,comp);
	for(int i=1;i<=m;i++)
	{
		int rootu=find(edge[i].u),rootv=find(edge[i].v);
		if(rootu!=rootv)
		{
			int newroot=++sz;
			fa[rootu]=fa[rootv]=fa[newroot]=newroot;
			fat[rootu][0]=fat[rootv][0]=newroot;
			val[rootu][0]=val[rootv][0]=edge[i].w;
			dis[newroot]=std::min(dis[rootu],dis[rootv]);
		}
	}
	for(int j = 1;j<=18;j++) 
     for(int i=1;i<=sz;i++)
      fat[i][j]=fat[fat[i][j-1]][j-1],val[i][j]=std::min(val[i][j-1],val[fat[i][j-1]][j-1]);   
}
inline int slove(int nv,int np)
{
	for(int i=18;i>=0;i--) 
	 if(val[nv][i]>np) nv=fat[nv][i];
    //printf("S:%d\n",nv);
    return dis[nv];
}
inline void work()
{
	memset(head,0,sizeof(head));
	//memset(val,0x3f,sizeof(val));
	memset(fat,0,sizeof(fat));
	cnt=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int u,v,l,a;
		scanf("%d%d%d%d",&u,&v,&l,&a);
		addedge(u,v,l),addedge(v,u,l);
		edge[i]=(node){u,v,a};
	}
	dijskra(1);
	kruskal();
	int q,k,s,lastans=0;
	scanf("%d%d%d",&q,&k,&s);
	for(int i=1;i<=q;i++)
	{
		int v0,p0;
		scanf("%d%d",&v0,&p0);
		int nv=(v0+k*lastans-1)%n+1;
		int np=(p0+k*lastans)%(s+1);
		printf("%d\n",lastans=slove(nv,np));
	}
}
int main()
{
	//freopen("1.txt","w",stdout); 
	int t;
	scanf("%d",&t);
	while(t--) work();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值