LG P4768 [NOI2018] 归程
Description
本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定。
魔力之都可以抽象成一个 $n$ 个节点、$m$ 条边的无向连通图(节点的编号从 $1$ 至 $n$)。我们依次用 $l,a$ 描述一条边的**长度、海拔**。
作为季风气候的代表城市,魔力之都时常有雨水相伴,因此道路积水总是不可避免的。由于整个城市的排水系统连通,因此**有积水的边一定是海拔相对最低的一些边**。我们用**水位线**来描述降雨的程度,它的意义是:所有海拔**不超过**水位线的边都是**有积水**的。
Yazid 是一名来自魔力之都的 OIer,刚参加完 ION2018 的他将踏上归程,回到他温暖的家。Yazid 的家恰好在魔力之都的 $1$ 号节点。对于接下来 $Q$ 天,每一天 Yazid 都会告诉你他的出发点 $v$ ,以及当天的水位线 $p$。
每一天,Yazid 在出发点都拥有一辆车。这辆车由于一些故障不能经过有积水的边。Yazid 可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。
需要特殊说明的是,第二天车会被重置,这意味着:
- 车会在新的出发点被准备好。
- Yazid 不能利用之前在某处停放的车。
Yazid 非常讨厌在雨天步行,因此他希望在完成回家这一目标的同时,最小化他**步行经过的边**的总长度。请你帮助 Yazid 进行计算。
本题的部分测试点将强制在线,具体细节请见【输入格式】和【子任务】。
Solution
将边以海拔从高到低加入可持久化并查集,如果预处理出每个点到1的最短路,那么询问点连通块内最短路的最小值就是答案
查询时二分找到在哪个线段树上
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<queue> using namespace std; int n,m,head[200005],tot,sav[800005],T,q,K,S,dep[20000005],lc[20000005],rc[20000005],cnt,val[20000005],lasans,rt[800005]; unsigned int dist[200005],minn[20000005]; bool vst[200005]; struct Edge{ int to,nxt; unsigned int w; }edge[800005]; struct Node{ int u,v,a; bool operator <(const Node &z)const{return a<z.a;} }node[800005]; struct Ele{ unsigned int x,val; bool operator <(const Ele &z)const{return val>z.val;} }; inline int read(){ int w=0,f=1; char ch=0; while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar(); return w*f; } void dijkstra(int s){ priority_queue<Ele>q; q.push((Ele){s,0}); while(q.size()){ Ele u=q.top(); q.pop(); if(vst[u.x])continue; dist[u.x]=u.val,vst[u.x]=true; for(int i=head[u.x];i;i=edge[i].nxt){ int v=edge[i].to; if(!vst[v])q.push((Ele){v,edge[i].w+u.val}); } } } void build(int &i,int l,int r){ if(!i)i=++cnt; if(l==r){val[i]=l,minn[i]=dist[l];return;} int mid=l+r>>1; build(lc[i],l,mid),build(rc[i],mid+1,r); } int ask(int i,int l,int r,int p){ if(l==r)return i; int mid=l+r>>1; if(p<=mid)return ask(lc[i],l,mid,p); else return ask(rc[i],mid+1,r,p); } int find(int i,int x){ int ret=ask(i,1,n,x); while(val[ret]!=x)x=val[ret],ret=ask(i,1,n,x); return ret; } int update(int &i,int j,int l,int r,int p){ i=++cnt; if(l==r)return i; int mid=l+r>>1; lc[i]=lc[j],rc[i]=rc[j]; if(p<=mid)return update(lc[i],lc[j],l,mid,p); else return update(rc[i],rc[j],mid+1,r,p); } int main(){ for(int t=read();t;t--){ n=read(),m=read(),cnt=tot=lasans=0,memset(head,0,sizeof(head)),memset(rt,0,sizeof(rt)),memset(minn,0,sizeof(minn)),memset(dep,0,sizeof(dep)),memset(val,0,sizeof(val)),memset(vst,false,sizeof(vst)),memset(lc,0,sizeof(lc)),memset(rc,0,sizeof(rc)); for(int i=1;i<=m;i++){ int u=read(),v=read(),l=read(),a=read(); edge[++tot]=(Edge){v,head[u],l},head[u]=tot,edge[++tot]=(Edge){u,head[v],l},head[v]=tot,node[i]=(Node){u,v,a}; } dijkstra(1),q=read(),K=read(),S=read(),sort(node+1,node+m+1); for(int i=1;i<=m;i++)sav[i]=node[i].a; sav[m+1]=S+1,sort(sav+1,sav+m+2),T=unique(sav+1,sav+m+2)-sav-1,build(rt[T],1,n); for(int i=T-1,pos=m;i;i--){ rt[i]=rt[i+1]; for(;pos&&node[pos].a==sav[i];pos--){ int u=find(rt[i],node[pos].u),v=find(rt[i],node[pos].v); if(val[u]!=val[v]){ if(dep[u]>dep[v])swap(u,v); val[update(rt[i],rt[i],1,n,val[u])]=val[v]; int w=update(rt[i],rt[i],1,n,val[v]); val[w]=val[v],minn[w]=min(minn[u],minn[v]),dep[w]=dep[v]+(dep[u]==dep[v]); } } } for(;q;q--){ int u=(read()+K*lasans-1)%n+1,p=(read()+K*lasans)%(S+1); printf("%u\n",lasans=minn[find(rt[upper_bound(sav+1,sav+T+1,p)-sav],u)]); } } return 0; }