传送门:https://www.luogu.org/problem/show?pid=4768
题目大意:给定无向图,每条边有长度和高度,多次询问从某个点pi出发到1号点的最短路,并给出参数qi,规定所有能从pi出发仅通过高度>qi的边到达的点视为可以不耗费代价地到达。
考场上写了一堆部分分,在距离比赛结束还有7分钟的时候想出了正解然而没时间写了……80分滚粗嘤嘤嘤。
正解好像叫做“kruskal重构树”,不过本蒟蒻表示没听说过这个名词,那就尽量用通俗易懂的语言讲明白这是个什么东西吧。
如果不强制在线,有一种比较直观的做法:按高度降序排序后,每次加入一条边相当于合并两个连通块,询问相当于查询当前连通块中dis的最小值,显然可以直接用并查集维护。
然而这题要强制在线,咋办?可持久化并查集?
其实是可以的,因为 只有访问历史版本没有修改,可以通过一些操作使得复杂度控制在一个log,不过本蒟蒻并不会写……
(好像直接写普通的可持久化并查集再卡卡常也能过的样子)。
当然这东西比较烦谁都不想写,那么还可以怎样?利用“只有访问历史版本没有修改”的性质,我们可以设计如下算法:
在每次合并时,新建一个节点,将这两个连通块合并上去,同时记录当前节点的dis是两个集合的dis的min,高度是当前边的高度。
这样预处理完之后,整张图会被建成一棵树,原始节点是叶子,高度信息是从叶子到根递减的。
于是,对于一组询问,我们可以直接倍增找到对应的合并节点,然后直接查询dis即可。
复杂度O(nlogn)。
还有一个小插曲:求最短路时一定要写dij,至于spfa?很遗憾它死了。
#include
using namespace std;
int t,n,m;
#define li long long
#define gc getchar()
#define pc putchar
int read(){
int x = 0,c = gc;
while(!isdigit(c)) c = gc;
while(isdigit(c)){
x = (x << 1) + (x << 3) + (c ^ '0');
c = gc;
}
return x;
}
void print(int x){
if(x >= 10) print(x / 10);
pc(x % 10 + '0');
}
struct edge{
int fr,to,nxt,val,hg;
}e[800010];
int ct,cnt,tc,fir[200010];
struct eg{
int fr,to,hg;
}o[400010];
void ins(int u,int v,int w,int x){
e[++cnt].fr = u;e[cnt].to = v;e[cnt].nxt = fir[u];fir[u] = cnt;e[cnt].val = w;e[cnt].hg = x;
e[++cnt].fr = v;e[cnt].to = u;e[cnt].nxt = fir[v];fir[v] = cnt;e[cnt].val = w;e[cnt].hg = x;
o[++ct].fr = u;o[ct].to = v;o[ct].hg = x;
}
bool operator < (eg q,eg w){
return q.hg > w.hg;
}
int f[400010],vl[400010];
int fa[400010];
int st[20][400010],mn[400010];
li dis[200010];
const int inf = 2007654321;
int p,k,s;
li as;
priority_queue > q;
#define mp make_pair
#define fi first
#define se second
void dij(){
int i,j;
for(i = 2;i <= n;++i) dis[i] = inf;
dis[1] = 0;q.push(mp(0,1));
while(!q.empty()){
pair t = q.top();q.pop();
if(-t.fi != dis[t.se]) continue;
for(i = fir[t.se];i;i = e[i].nxt){
j = e[i].to;
if(-t.fi + e[i].val < dis[j]){
dis[j] = -t.fi + e[i].val;
q.push(mp(-dis[j],j));
}
}
}
}
int getf(int q){
return f[q] == q ? q : f[q] = getf(f[q]);
}
void mg(int u,int v,int w){
int x = getf(u),y = getf(v);
if(x == y) return;
f[x] = f[y] = st[0][x] = st[0][y] = ++tc;
mn[tc] = w;
vl[tc] = min(vl[x],vl[y]);
}
int lgo[400010],dpt[400010];
void buildst(){
register int i,j;
for(i = 1;i <= lgo[tc];++i){
for(j = 1;j <= tc;++j) st[i][j] = st[i - 1][st[i - 1][j]];
}
}
void dfs(int q){
if(dpt[q] || q == tc) return;
dfs(st[0][q]);
dpt[q] = dpt[st[0][q]] + 1;
}
int main(){
int u,v,w,x;
int i,j;
for(i = 2;i <= 400000;++i) lgo[i] = lgo[i >> 1] + 1;
t = read();
while(t--){
memset(fir,0,sizeof(fir));ct = cnt = as = 0;
n = read();m = read();tc = n;
for(i = 1;i <= m;++i){
u = read();v = read();w = read();x = read();ins(u,v,w,x);
}
dij();
sort(o + 1,o + ct + 1);
for(i = 1;i <= n * 2;++i) f[i] = i,fa[i] = dpt[i] = 0;
for(i = 1;i <= n;++i) vl[i] = dis[i];
for(i = 1;i <= ct;++i) mg(o[i].fr,o[i].to,o[i].hg);
buildst();
for(i = 1;i <= tc;++i) if(!dpt[i]) dfs(i);
p = read();k = read();s = read();
int tot = 0;
for(i = 1;i <= p;++i){
u = read();v = read();
u = (u + k * as - 1) % n + 1;
v = (v + k * as) % (s + 1);
for(j = lgo[dpt[u]];j >= 0;--j) mn[st[j][u]] > v ? u = st[j][u] : 0;
print(as = vl[u]);pc('\n');
}
}
return 0;
}