【NOI 2018】歸程

題目描述

本題的故事發生在魔力之都,在這裏我們將爲你介紹一些必要的設定。 魔力之都可以抽象成一個 nn 個節點、mm 條邊的無向連通圖(節點的編號從 11nn)。我們依次用 l,al,a 描述一條邊的長度、海拔。 作爲季風氣候的代表城市,魔力之都時常有雨水相伴,因此道路積水總是不可避免 的。由於整個城市的排水系統連通,因此有積水的邊一定是海拔相對最低的一些邊。我們用水位線來描述降雨的程度,它的意義是:所有海拔不超過水位線的邊都是有積水的。

Yazid 是一名來自魔力之都的 OIer,剛參加完 ION2018 的他將踏上歸程,回到他 溫暖的家。 Yazid 的家恰好在魔力之都的 11 號節點。對於接下來 QQ 天,每一天Yazid 都會告訴你他的出發點 vv,以及當天的水位線 pp。 每一天,Yazid 在出發點都擁有一輛車。這輛車由於一些故障不能經過有積水的邊。 Yazid 可以在任意節點下車,這樣接下來他就可以步行經過有積水的邊。但車會被留在他下車的節點並不會再被使用。 需要特殊說明的是,第二天車會被重置,這意味着:

車會在新的出發點被準備好。
Yazid 不能利用之前在某處停放的車。 Yazid 非常討厭在雨天步行,因此他希望在完成回家這一目標的同時,最小化他步行經過的邊的總長度。請你幫助 Yazid 進行計算。 本題強制在線。

n2×105n\le 2\times 10^5m4×105m\le 4\times 10^5Q4×105Q\le 4\times 10^5l104l\le 10^4a109a\le 10^9

算法分析

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章