我們可以把從v到1的路徑分成兩部分,一半全開車,一半全走路
也就是說要枚舉n個節點作爲斷點(假設當前斷點爲u),這個斷點是可行解與最優解當且僅當存:在一條從v到u的路徑可以全部開車且從u到1全部走路的最短路是滿足上一條件中最短的
從v出發開車可以到的點, 一定滿足路徑上所有邊海拔都高於水位
Kruskal重構樹:
這裏已經很明顯可以用Kruskal重構樹求解了
我們把每條邊按海拔降序排序,重構樹完成後,對於每次詢問,找到樹中深度最小且海拔大於水位的節點,那麼他的子樹的全部節點都可以由v開車到達
這一點可以由重構樹是小根堆的性質簡單得證,即每個節點子樹內所有節點海拔都比該節點大
現在要求的最後一步就是這個子樹內所有節點到1號節點的步行最短路, 因爲是無向圖,所以一開始預處理1節點到所有節點最短路就好,然後dfs可以順便求出某個節點子樹內的最短路
--
\(ps\):
這個題卡 \(SPFA\) CCF怎麼能這樣對我可愛的SPFA
所以必須打迪傑斯特拉
處理細節:
- 莫名卡常(可能是我不優秀),對於一些數組不用清零的就別清了,比如倍增數組和最小值數組(刪了HSYOJ就過了)
- 克魯斯卡爾重構樹套路:清零!!!(數據不清空,爆零兩行淚)
可持久化並查集
一定程度上,可持久化並查集的適用範圍是大於克魯斯卡爾重構樹的,雖然在這裏不是最優秀的做法,把\(NlogN\)的複雜度硬生生變成了\(Nlog^2N\)(卡卡常也是能過的),這裏雖然不是正解,但也是一個好方案
做法:
迪傑斯特拉預處理,然後可持久化並查集維護聯通塊(口胡完了)
實現細節:
注意常數
代碼:
(這裏用的是Kruskal重構樹)
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define ll long long
#define re register
#define gc getchar()
inline int read()
{
re int x(0),f(1);re char ch(gc);
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=gc;}
while(ch>='0'&&ch<='9') x=(x*10)+(ch^48),ch=gc;
return x*f;
}
const int N=8e5+10;
int h[N],cnt,n,m,f[N][22],tot;
ll val[N],_min[N],dis[N];
struct edge{int next,to;ll w;}e[N<<1];
void add(int u,int v,ll w){e[++cnt]=(edge){h[u],v,w},h[u]=cnt;}
#define QXX(u) for(int i=h[u],v;v=e[i].to,i;i=e[i].next)
struct node{int u,v;ll h;}E[N<<1];
bool operator < (node a,node b){return a.h>b.h;}
struct Node{ll x;int id;};
bool operator < (Node a,Node b){return a.x<b.x;}
priority_queue <Node> q;
int vis[N];
int fa[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void djs()
{
memset(vis,0,sizeof(vis));
memset(dis,127,sizeof(dis));
q.push((Node){0,1});
dis[1]=0;
while(!q.empty())
{
Node t=q.top();q.pop();
int u=t.id;
if(vis[u]) continue;
vis[u]=1;
QXX(u)
if(dis[u]+e[i].w<dis[v])
{
dis[v]=dis[u]+e[i].w;
q.push((Node){-dis[v],v});
}
}
}
void dfs(int u)
{
_min[u]=dis[u];
QXX(u)
{
f[v][0]=u;
dfs(v);
_min[u]=min(_min[u],_min[v]);
}
}
void kruskal()
{
memset(h,0,sizeof(h));
cnt=0;
sort(E+1,E+1+m);
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i)
{
int u=find(E[i].u),v=find(E[i].v);
if(u!=v)
{
val[++tot]=E[i].h;
fa[u]=fa[v]=fa[tot]=tot;
add(tot,u,0),add(tot,v,0);
}
}
dfs(tot);
}
int main()
{
// freopen("return.in","r",stdin);
int T=read();
while(T--)
{
memset(h,0,sizeof(h));
memset(f,0,sizeof(f));
memset(_min,127,sizeof(_min));
n=read(),m=read(),cnt=0;tot=n;
for(int i=1;i<=m;++i)
{
re int u=read(),v=read(),w=read(),h=read();
add(u,v,w),add(v,u,w);
E[i].u=u,E[i].v=v,E[i].h=h;
}
djs();
kruskal();
for(int i=1;(1<<i)<=tot;i++)
for(int u=1;u<=tot;u++)
f[u][i]=f[f[u][i-1]][i-1];
int Q=read(),K=read(),S=read();
ll la=0;
while(Q--)
{
int vi=read(),pi=read();
vi=(vi+K*la-1)%n+1;
pi=(pi+K*la)%(S+1);
for(int j=21;j>=0;--j)
if(f[vi][j]&&val[f[vi][j]]>pi)
vi=f[vi][j];
la=_min[vi];
cout<<la<<endl;
}
}
return 0;
}