作为NOI Day1 的T1,这道题目还是比较清真的(虽然自己在同步赛的时候只打了70分的部分分,然后数组开小了挂成了55分。。)正解的方法似乎有很多,可持久化并查集什么的都可以做。但个人认为还是Kruskal重构树的方法比较好些,也比较容易一点。 Kruskal重构树的基础还是像Kruskal生成树一样,先把边sort,再一条一条加进生成树中。而重构树唯一不同的点是当重构树在进行两个联通块合并的时候,并不是直接将其合并,而是新建一个父节点,其点权为当前枚举到的边的边权。这样显然的Kruskal重构树就会有两个比较重要的性质了:
①这颗重构树是个二叉树(虽然在这里并没有什么用)
②这棵树是个大(小)根堆 知道了这一点我们就可以开始做这道题了。我们先按海拔从大到小将每条边的排序,然后做Kruskal重构树,这样我们生成的Kruskal重构树就是一棵以海拔为关键字的小根堆,同时他会有一些性质:如果一条边被水淹没了(在重构树中就是那条边所对应的点),那么这个节点的所有父节点都会被水淹没,并且如果这条边没有被水淹没,那么它的子树的任意一个点也不会被水淹没,于是这棵子树的任意两个点都可以通过开车到达。于是我们就可以先跑一边最短路(出题人:SPFA死掉了),然后在进行构造Kruskal重构树的时候维护子树中到根的最短距离Mn。对于每一个询问,我们只需要倍增在重构树上找到未被水淹没的深度最浅的节点,然后返回这个节点的Mn的值就行了,还是比较好写的。
#pragma GCC optimize (2)
#include
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=4e5+500;
typedef pairint>P;
#define fi first
#define se second
int n,m,T,tot,waytot,ndcnt,Q,k,s,cnt;
ll ans;
int head[maxn],fa[maxn<<1],val[maxn<<1],Head[maxn<<1],deep[maxn<<1],p[maxn][22];
ll dis[maxn],Mn[maxn<<1];
bool vis[maxn];
struct edge {
int to,nxt,l,h;
}E[maxn<<2];
struct sortedge {
int f,t,l,h;
bool operator < (const sortedge &rhs) const {
return h>rhs.h;
}
}EE[maxn<<2];
struct way {
int to,nxt;
}G[maxn<<4];
/*==================Define Area================*/
void FO() {
freopen("return.in","r",stdin);
freopen("return.out","w",stdout);
}
namespace doit {
void init() {
ans=tot=waytot=cnt=0;
memset(head,-1,sizeof head);
memset(Head,-1,sizeof Head);
for(int i=1;i<=200000;i++) {
fa[i]=i;
}
}
void addedge(int u,int v,int h,int l) {
E[++tot].to=v;E[tot].nxt=head[u];head[u]=tot;E[tot].h=h;E[tot].l=l;
E[++tot].to=u;E[tot].nxt=head[v];head[v]=tot;E[tot].h=h;E[tot].l=l;
}
void Dj() {
priority_queuevector
,greater
>Q;
while(!Q.empty()) Q.pop();
memset(vis,0,sizeof vis);
memset(dis,0x7f,sizeof dis);
dis[1]=0;
Q.push(P(dis[1],1));
while(!Q.empty()) {
P p=Q.top();Q.pop();
int idx=p.se;
ll dist=p.fi;
vis[idx]=1;
for(int i=head[idx];~i;i=E[i].nxt) {
int to=E[i].to;
if(vis[to]) continue;
if(dis[to]>dist+E[i].l) {
dis[to]=dist+E[i].l;
Q.push(P(dis[to],to));
}
}
}
}
}
namespace KruskalTree {
void addway(int u,int v) {
G[++waytot].to=v;G[waytot].nxt=Head[u];Head[u]=waytot;
G[++waytot].to=u;G[waytot].nxt=Head[v];Head[v]=waytot;
}
int find(int x) {
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void unite(int x,int y,int v) {
int fx=find(x),fy=find(y);
if(fx==fy) return ;
++ndcnt;
addway(fx,ndcnt);
addway(fy,ndcnt);
Mn[ndcnt]=min(Mn[fx],Mn[fy]);
fa[ndcnt]=ndcnt;
fa[fx]=ndcnt;
fa[fy]=ndcnt;
val[ndcnt]=v;
return ;
}
void Kruskal() {
ndcnt=n;
for(int i=1;i<=n;i++) {
Mn[i]=dis[i];
}
for(int i=1;i<=cnt;i++) {
unite(EE[i].f,EE[i].t,EE[i].h);
}
}
}
namespace LCA {
void dfs(int o,int ff,int dep) {
deep[o]=dep;
p[o][0]=ff;
for(int i=Head[o];~i;i=G[i].nxt) {
int to=G[i].to;
if(to==ff) continue;
dfs(to,o,dep+1);
}
}
void Cal() {
for(int i=1;i<=20;i++) {
for(int j=1;j<=ndcnt;j++) {
p[j][i]=p[p[j][i-1]][i-1];
}
}
}
int Solve(ll o,ll a) {
for(int i=20;i>=0;i--) {
if(p[o][i]&&val[p[o][i]]>a) o=p[o][i];
}
return o;
}
}
using namespace doit;
using namespace KruskalTree;
using namespace LCA;
int main() {
FO();
read(T);
while(T--) {
init();
read(n);read(m);
for(int i=1,u,v,h,l;i<=m;i++) {
read(u);read(v);read(l);read(h);
addedge(u,v,h,l);
EE[++cnt].f=u;EE[cnt].t=v;EE[cnt].l=l;EE[cnt].h=h;
}
Dj();
sort(EE+1,EE+1+cnt);
Kruskal();
dfs(ndcnt,0,1);
Cal();
read(Q);read(k);read(s);
while(Q--) {
ll v,p;
read(v);read(p);
v=(v+k*ans-1)%n+1;
p=(p+k*ans)%(s+1);
int o=Solve(v,p);
ans=Mn[o];
printf("%lld\n",ans);
}
}
}