luogu P4768 [NOI2018]归程

背景:

今天真得好累。

题目传送门:

https://www.luogu.org/problem/P4768

题意:

n n n个点, m m m条边,有边权,一个表示长度,一个表示高度。
多组询问,每组询问问从 x x x点出发,当前的积水高度是 y y y,可以开车经过边的高度必须严格大于 y y y,当遇到边的高度小于等于 y y y时,就会弃掉汽车,选择步行,求步行到 1 1 1号点的最小距离。

思路:

显然的克鲁斯卡尔重构树的应用。
不弃车时一定是一个联通块,而且是最大的联通块,而答案一定是这个联通块中到 1 1 1点的最小距离。
那么我们先用迪杰斯特拉(丧心病狂的出题人卡 spfa \text{spfa} spfa)预处理出每一个点到 1 1 1号点的最短路,然后按照边的高度降序(因为高度越高越好),用克鲁斯卡尔重构树将这些边处理。最后倍增找到一个尽可能远的祖先,然后在这个子树内寻找距离的最小值即可。
建议大家在 loj \text{loj} loj上交,可以评测所有的数据,但是要用文件输入输出。

代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define I inline
#define R register
using namespace std;
struct node3
{
	int x,d;
	friend bool operator <(node3 x,node3 y)
	{
		return x.d>y.d;
	}
};
priority_queue<node3> F;
	struct node1{int x,y,l,next;} a[800010];
	struct node2{int x,y,z,l;} b[800010];
	int last[400010],val[400010],fa[400010],f[400010][20],dis[400010],son[400010][2];
	bool bz[400010];
	int n,m,q,op,p,len;
bool cmp(node2 x,node2 y)
{
	return x.z>y.z;
}
I void clear()
{
	len=0;
	memset(last,0,sizeof(last));
	memset(fa,0,sizeof(fa));
	memset(f,0,sizeof(f));
}
I int find(int x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
I void ins(int x,int y,int l)
{
	a[++len]=(node1){x,y,l,last[x]}; last[x]=len;
}
I void spfa()
{
	memset(dis,63,sizeof(dis));
	dis[1]=0;
	memset(bz,false,sizeof(bz));
	F.push((node3){1,0});
	while(!F.empty())
	{
		node3 TOP=F.top();
		F.pop();
		if(bz[TOP.x]) continue;
		bz[TOP.x]=true;
		for(R int i=last[TOP.x];i;i=a[i].next)
		{
			int y=a[i].y;
			if(dis[y]>dis[TOP.x]+a[i].l)
			{
				dis[y]=dis[TOP.x]+a[i].l;
				F.push((node3){y,dis[y]});
			}
		}
	}
}
void dfs(int x)
{
	if(x<=n) return;
	f[son[x][0]][0]=x,f[son[x][1]][0]=x;
	dfs(son[x][0]),dfs(son[x][1]);
	dis[x]=min(dis[son[x][0]],dis[son[x][1]]);
}
I void init()
{
	spfa();
	sort(b+1,b+m+1,cmp);
	int cnt=n;
	for(R int i=1;i<=m;i++)
	{
		int t1=find(b[i].x),t2=find(b[i].y);
		if(t1==t2) continue;
		fa[++cnt]=cnt;
		fa[t1]=fa[t2]=cnt;
		val[cnt]=b[i].z;
		son[cnt][0]=t1,son[cnt][1]=t2;
		if(cnt==n+n-1) break;
	}
	dfs(cnt);
	for(R int i=1;i<=18;i++)
		for(R int j=1;j<=cnt;j++)
			f[j][i]=f[f[j][i-1]][i-1];
}
I int find_(int x,int y)
{
	for(R int i=18;i>=0;i--)
		if(f[x][i]&&val[f[x][i]]>y) x=f[x][i];
	return x;
}
I char getc()
{
	static char buf[1<<20],*fs,*ft;
	return(fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin)),fs==ft)?EOF:*fs++;
}
I void read(int &x)
{
	char ch=getc(),f=1;
	for(x=0;ch<'0'||ch>'9';ch=getc());
	for(;ch>='0'&&ch<='9';ch=getc())
		x=((x+(x<<2))<<1)+(ch^0x30);
}
static const int BUF=50000000;
char buf[BUF],*h=buf;
inline void put(char ch)
{
    h==buf+BUF?(fwrite(buf,1,BUF,stdout),h=buf):0;
    *h++=ch;
}
inline void putint(int num)
{
    static char _buf[30];
    sprintf(_buf,"%d",num);
    for(char *s=_buf;*s;s++)put(*s);
}
inline void finish()
{
    fwrite(buf,1,h-buf,stdout);
}
int main()
{
	int T,x,y,lastans;
	read(T);
	while(T--)
	{
		clear();
		read(n),read(m);
		for(R int i=1;i<=n;i++)
			fa[i]=i;
		for(R int i=1;i<=m;i++)
		{
			read(b[i].x),read(b[i].y),read(b[i].l),read(b[i].z);
			ins(b[i].x,b[i].y,b[i].l),ins(b[i].y,b[i].x,b[i].l);
		}
		init();
		read(q),read(op),read(p);
		lastans=0;
		for(R int i=1;i<=q;i++)
		{
			read(x),read(y);
			x=(x+op*lastans-1)%n+1;
			y=(y+op*lastans)%(p+1);
			int t=find_(x,y);
			putint(lastans=dis[find_(x,y)]),put('\n');
		}
	}
	finish();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值