题目大意:一个n个节点、m条边的无向连通图。我们依次用l,a描述一条边的长度、海拔。对于接下来Q个询问,每一天Yazid都会告诉你他的出发点v,Yazid需要从v到1,以及当天的水位线p,所有海拔不超过水位线的边都是有积水的。
每一个询问,Yazid在出发点都拥有一辆车。这辆车不能经过有积水的边。Yazid可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。
需要特殊说明的是,第二天车会被重置,这意味着:
- 车会在新的出发点被准备好。
- Yazid不能利用之前在某处停放的车。
最小化Yazid的步行距离,强制在线
NOI2018的毒瘤题目,本以为自己能凎出来,但是还是看了题解,妙啊
对于最优解(1,v)这条路径中,一定存在一个点u,使得(1,u)全为步行,(u,v)全为开车,由此我们可以暴力枚举分界点u
但是怎么处理积水的边呢?这是一个经典的问题,形如从点v开始只经过边权小于(或大于)x的路径所能到达节点集合,可以用kruskal重构树。
在kruskal重构树中,从点v倍增向上跳,找到深度最小的一个点权大于p的节点,p的子树就是v可达的子集。
预处理出1到所有点的最短路,再预处理出重构树里每个子树内到1最短节点的距离就OK了。
还有一件事,关于SPFA,它死了
#include<iostream> #include<iomanip> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> using namespace std; inline int read() { char ch; int bj=1; while(!isdigit(ch=getchar())) bj=(ch=='-')?-1:1; int res=ch^(3<<4); while(isdigit(ch=getchar())) res=(res<<1)+(res<<3)+(ch^(3<<4)); return res*bj; } void printnum(int x) { if(x>9)printnum(x/10); putchar(x%10+'0'); } inline void print(int x,char ch) { if(x<0) { putchar('-'); x=-x; } printnum(x); putchar(ch); } const int MAXN=2e5+5; int n,m,tot,cnt,g[MAXN<<1][25],cnt1; int lastans; int a[MAXN<<1]; int h[MAXN]; int h1[MAXN<<1]; struct Edge { int to,nxt,v; } w[MAXN<<2]; //图里的边 struct node { int to,nxt; } w1[MAXN<<2]; //树里的边 int d[MAXN<<1]; bool vst[MAXN];//最短路 int prt[MAXN<<1];//并查集 int f[MAXN<<1]; priority_queue<pair<int,int> >q; struct node1 { int x,y,a; inline bool operator < (node1 b)const { return a>b.a; } } e[MAXN<<1]; inline void AddEdge(int x,int y,int z) {//建图边 w[++cnt].to=y; w[cnt].nxt=h[x]; w[cnt].v=z; h[x]=cnt; } inline void AddEdge2(int x,int y) {//建树边 w1[++cnt1].to=y; w1[cnt1].nxt=h1[x]; h1[x]=cnt1; } inline void dijkstra(int v0) { memset(d,0x3f,sizeof(d)); memset(vst,0,sizeof(vst)); d[v0]=0; q.push(make_pair(0,v0)); while(!q.empty()) { int x=q.top().second; q.pop(); if(vst[x])continue; vst[x]=1; for(int i=h[x]; i; i=w[i].nxt) { int v=w[i].to; if(d[v]>d[x]+w[i].v) { d[v]=d[x]+w[i].v; q.push(make_pair(-d[v],v)); } } } } int GetFather(int x) { return x^prt[x]?prt[x]=GetFather(prt[x]):x; } inline void kruskal() { sort(e+1,e+m+1); tot=n; for(int i=1; i<=n; i++)prt[i]=i; for(int i=1; i<=m; i++) { int x=GetFather(e[i].x),y=GetFather(e[i].y); if(x^y) { tot++; a[tot]=e[i].a; prt[x]=prt[y]=prt[tot]=tot; AddEdge2(x,tot); AddEdge2(tot,x); AddEdge2(y,tot); AddEdge2(tot,y); } } } inline void DFS(int x,int fa) { f[x]=d[x]; g[x][0]=fa; for(int i=h1[x]; i; i=w1[i].nxt) { int v=w1[i].to; if(v==fa)continue; DFS(v,x); f[x]=min(f[x],f[v]); } } inline void ST() { for(int j=1; (1<<j)<=tot; j++) for(int i=1; i<=tot; i++) if(g[i][j-1]) g[i][j]=g[g[i][j-1]][j-1]; } inline void clear() { memset(h,0,sizeof(h)); memset(w,0,sizeof(w)); memset(h1,0,sizeof(h1)); memset(w1,0,sizeof(w1)); cnt=cnt1=0; memset(g,0,sizeof(g)); memset(prt,0,sizeof(prt)); lastans=0; } inline void Solve() { n=read(); m=read(); int x,y,z; for(int i=1; i<=m; i++) { e[i].x=read(); e[i].y=read(); z=read(); e[i].a=read(); AddEdge(e[i].x,e[i].y,z); AddEdge(e[i].y,e[i].x,z); } dijkstra(1); kruskal(); DFS(tot,0); ST(); int Q=read(); int k=read(),s=read(); while(Q--) { x=(read()+k*lastans-1)%n+1; y=(read()+k*lastans)%(s+1); for(int i=24; ~i; i--) if(g[x][i]&&a[g[x][i]]>y)x=g[x][i]; print(lastans=f[x],' '); } } signed main() { int T=read(); while(T--) { clear(); Solve(); } return 0; }