【NOI 2018】归程

xiaoxiao2025-04-11  11

题目描述

本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定。 魔力之都可以抽象成一个 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; }
转载请注明原文地址: https://www.6miu.com/read-5028013.html

最新回复(0)