【NOI 2018】归程

3 篇文章 0 订阅
2 篇文章 0 订阅

题目描述

本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定。 魔力之都可以抽象成一个 n n n 个节点、 m m m 条边的无向连通图(节点的编号从 1 1 1 n n n)。我们依次用 l , a l,a l,a 描述一条边的长度、海拔。 作为季风气候的代表城市,魔力之都时常有雨水相伴,因此道路积水总是不可避免 的。由于整个城市的排水系统连通,因此有积水的边一定是海拔相对最低的一些边。我们用水位线来描述降雨的程度,它的意义是:所有海拔不超过水位线的边都是有积水的。

Yazid 是一名来自魔力之都的 OIer,刚参加完 ION2018 的他将踏上归程,回到他 温暖的家。 Yazid 的家恰好在魔力之都的 1 1 1 号节点。对于接下来 Q Q Q 天,每一天Yazid 都会告诉你他的出发点 v v v,以及当天的水位线 p p p。 每一天,Yazid 在出发点都拥有一辆车。这辆车由于一些故障不能经过有积水的边。 Yazid 可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。 需要特殊说明的是,第二天车会被重置,这意味着:

车会在新的出发点被准备好。
Yazid 不能利用之前在某处停放的车。 Yazid 非常讨厌在雨天步行,因此他希望在完成回家这一目标的同时,最小化他步行经过的边的总长度。请你帮助 Yazid 进行计算。 本题强制在线。

n ≤ 2 × 1 0 5 n\le 2\times 10^5 n2×105 m ≤ 4 × 1 0 5 m\le 4\times 10^5 m4×105 Q ≤ 4 × 1 0 5 Q\le 4\times 10^5 Q4×105 l ≤ 1 0 4 l\le 10^4 l104 a ≤ 1 0 9 a\le 10^9 a109

算法分析

Dijkstra+Kruskal 重构树+倍增+DFS序+线段树。

和 【BZOJ 3551】Peaks 加强版 一样,先构出 Kruskal 求最大生成树的重构树,可以下车的位置就在一个子树内了,事先预处理出每个点到出发点的最短路,DFS 序+线段树维护子树区间范围最小值即可。

代码实现

#include <cstdio>
#include <cstring>
#include <queue>
#include <utility>
#include <vector>
#include <functional>
#include <algorithm>
typedef std::pair<int,int> P;
const int maxn=(int)2e5+5;
const int maxm=(int)4e5+5;
char buf[1<<15],*fs=buf,*ft=buf;
inline char gc() {
	if(fs==ft) {
		ft=(fs=buf)+fread(buf,1,1<<15,stdin);
		if(fs==ft) return 0;
	}
	return *fs++;
}
inline void read(int &num) {
	char c=gc();int f=false;num=0;
	while(c<'0'||'9'<c) {if(c=='-') f=true;c=gc();}
	while('0'<=c&&c<='9') {num=num*10+c-'0';c=gc();}
	if(f) num=-num;
}
struct edge {int u,v,w;} e[maxm],edges[maxm<<1];
int head[maxn<<1],nxt[maxm<<1],idx=0;
inline void clear() {idx=0;memset(head,0,sizeof(head));}
inline void add(int u,int v,int w=0) {
	edges[++idx]=(edge){u,v,w};
	nxt[idx]=head[u];head[u]=idx;
}
inline bool cmp(const edge &x,const edge &y) {return x.w>y.w;}
int fa[maxn<<1];int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);}
int d[maxn<<1],h[maxn<<1],pa[maxn<<1][20],tot[maxn<<1],dfn[maxn<<1],dl[maxn<<1],dfsidx=0;
void dfs(int x) {
	tot[x]=1;dl[dfn[x]=++dfsidx]=x;
	for(register int i=head[x];i;i=nxt[i]) {int v=edges[i].v;dfs(v);tot[x]+=tot[v];}
}
int min[maxn<<5];
inline void build(int o,int l,int r) {
	int mid=(l+r)>>1;
	if(l==r) min[o]=d[dl[mid]];
	else {
		build(o<<1,l,mid);build(o<<1|1,mid+1,r);
		min[o]=std::min(min[o<<1],min[o<<1|1]);
	}
}
inline int query(int o,int l,int r,int ql,int qr) {
	int mid=(l+r)>>1;
	if(ql<=l&&r<=qr) return min[o];
	int ans=0x7fffffff;
	if(ql<=mid) ans=std::min(ans,query(o<<1,l,mid,ql,qr));
	if(mid+1<=qr) ans=std::min(ans,query(o<<1|1,mid+1,r,ql,qr));
	return ans;
}
int main() {
	int t;scanf("%d",&t);
	while(t--) {
		int n,ln,m;read(n);read(m);ln=n;
		int u,v,l,a;clear();
		for(register int i=0;i<m;++i) {
			read(u);read(v);read(l);read(a);
			add(u,v,l);add(v,u,l);e[i]=(edge){u,v,a};
		}
		std::priority_queue<P,std::vector<P>,std::greater<P> > pq;
		memset(d,0x3f,sizeof(d));d[1]=0;pq.push(P(0,1));
		while(pq.size()) {
			P x=pq.top();pq.pop();int u=x.second;
			if(d[u]^x.first) continue;
			for(register int i=head[u];i;i=nxt[i]) {
				edge &e=edges[i];
				if(d[e.v]>d[u]+e.w) {
					d[e.v]=d[u]+e.w;
					pq.push(P(d[e.v],e.v));
				}
			}
		}
		std::sort(e,e+m,cmp);clear();
		for(int i=1;i<=(n<<1);++i) fa[i]=i;
		for(register int i=0;i<m;++i) {
			int x=find(e[i].u),y=find(e[i].v);if(x==y) continue;
			pa[x][0]=pa[y][0]=fa[x]=fa[y]=++n;h[n]=e[i].w;add(n,x);add(n,y);
		}
		for(register int i=1;i<20;++i) for(register int x=1;x<=n;++x) pa[x][i]=pa[pa[x][i-1]][i-1];
		dfsidx=0;dfs(n);build(1,1,n);
		int q,k,s,v0,p0,la=0;read(q);read(k);read(s);
		while(q--) {
			read(v0);read(p0);v0=(v0+k*la-1)%ln+1;p0=(p0+k*la)%(s+1);
			for(register int i=19;i>=0;--i) if(pa[v0][i]&&h[pa[v0][i]]>p0) v0=pa[v0][i];
			printf("%d\n",la=query(1,1,n,dfn[v0],dfn[v0]+tot[v0]-1));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值