在Kruskal算法求最小生成树时,按照边权排序,然后依次合并节点。
在构建Kruskal重构树时,按照边权排序,在合并节点 x , y x,y x,y时,断开 x , y x,y x,y之间的边,并新建节点 z z z, z z z的点权为边 ( x , y ) (x,y) (x,y)的权值, z z z向 x , y x,y x,y分别连边,并用并查集维护连通性。
void Kruskal(){
sort(edg+1, edg+1+m, cmp);
memset(e, 0, sizeof(e));
memset(head, 0, sizeof(head));
tot = 0;
for(int i = 1; i <= n; i++)
f[i] = i;
nodetot = n;
for(int i = 1; i <= m; i++){
int fx = find(edg[i].x);
int fy = find(edg[i].y);
if(fx == fy)
continue;
val[++nodetot] = edg[i].v;
f[nodetot] = f[fx] = f[fy] = nodetot;
add(nodetot, fx); add(nodetot, fy);
}
}
给定一个连通图,边有两个权值,长度 l l l,高度 h h h。每次询问给定起点 s 0 s_0 s0,水位线 h 0 h_0 h0,高度小于等于 h 0 h_0 h0的边只能走路,不能开车。终点为 1 1 1号结点,询问从 s 0 s_0 s0到终点走路的最小长度。
将所有边按照高度排序,构建 k r u s k a l kruskal kruskal重构树,得到的是小根堆。对于一棵子树 u u u,如果水位线 h 0 h_0 h0小于子树根节点的权值,该子树中的叶子结点是可以互相到达的,即原图中对应的点可以开车直达。
对于当前询问 ( s 0 , h 0 ) (s_0,h_0) (s0,h0),找到根节点 u u u,满足 w [ u ] > h 0 w[u] > h_0 w[u]>h0 且 w [ f a [ u ] ] ≤ h 0 w[fa[u]] \le h_0 w[fa[u]]≤h0,则 s 0 s_0 s0可以开车到达的结点为子树 u u u的叶子结点。
可以利用 D i j s k r a Dijskra Dijskra求出所有点到 1 1 1号结点的最短路。按照高度排序,构建 k r u s k a l kruskal kruskal重构树,然后 d f s dfs dfs求出子树 u u u中到 1 1 1号结点路径的最小值 d [ u ] d[u] d[u]。
对于每次询问 ( s 0 , h 0 ) (s_0,h_0) (s0,h0),利用倍增找到结点 u u u,答案为 d [ u ] d[u] d[u]
#include
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
#define mkp(a, b) make_pair(a, b)
#define fi first
#define se second
const int maxn = 2e5+10;
const int maxm = 4e5+10;
const int INF = 0x3f3f3f3f;
struct edge{
int to, nxt;
ll v;//长度
}e[maxm<<1];
int head[maxn<<1], tot;
void add(int a, int b, ll c = 0){
e[++tot].nxt = head[a];
e[tot].to = b;
e[tot].v = c;
head[a] = tot;
}
struct node{
int x, y, v;
bool operator < (const node &b) const{
return v > b.v;
}
}edg[maxm];
int f[maxn<<1];
int find(int x){
return x == f[x] ? x : f[x] = find(f[x]);
}
int nodetot;
int val[maxn<<1];
int n, m;
void Kruskal(){
memset(e, 0, sizeof(e));
memset(head, 0, sizeof(head));
tot = 0;
for(int i = 1; i <= n; i++)
f[i] = i;
nodetot = n;
for(int i = 1; i <= m; i++){
int fx = find(edg[i].x);
int fy = find(edg[i].y);
if(fx == fy)
continue;
val[++nodetot] = edg[i].v;
f[nodetot] = f[fx] = f[fy] = nodetot;
add(nodetot, fx); add(nodetot, fy);
}
}
int vis[maxn];
ll dis[maxn<<1];
priority_queue<pll> q;
void Dijkstra(int s = 1){
memset(vis, 0, sizeof(vis));
memset(dis, INF, sizeof(dis));
dis[1] = 0;
q.push(mkp(0, 1));
while(q.size()){
int u = q.top().se; q.pop();
if(vis[u])
continue;
vis[u] = 1;
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].to;
if(dis[v] > dis[u]+e[i].v){
dis[v] = dis[u]+e[i].v;
q.push(mkp(-dis[v], v));
}
}
}
}
int p[maxn<<1][20];
ll d[maxn<<1];
void dfs(int u, int fath){
p[u][0] = fath;
d[u] = dis[u];
for(int i = 1; i <= 19; i++)
p[u][i] = p[p[u][i-1]][i-1];
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].to;
dfs(v, u);
d[u] = min(d[u], d[v]);
}
}
int find_node(int u, int x){
int v = u;
for(int i = 19; i >= 0; i--)
if(val[p[v][i]] > x)
v = p[v][i];
return v;
}
int Q, K, S;
void init(){
memset(head, 0, sizeof(head));
tot = 0;
memset(e, 0, sizeof(e));
memset(p, 0, sizeof(p));
memset(val, 0, sizeof(val));
while(q.size())
q.pop();
}
void solve(){
cin >> n >> m;
init();
for(int i = 1; i <= m; i++){
int x, y, v, h;
cin >> x >> y >> v >> h;
edg[i].x = x; edg[i].y = y; edg[i].v = h;
add(x, y, v); add(y, x, v);
}
Dijkstra();
sort(edg+1, edg+1+m);
Kruskal();
d[0] = -1;
dfs(nodetot, 0);
cin >> Q >> K >> S;
ll last = 0;
while(Q--){
int v0, p0, v, p;
cin >> v0 >> p0;
v = (v0+K*last-1)%n+1;
p = (p0+K*last)%(S+1);
int x = find_node(v, p);
last = d[x];
//cout << "ans = ";
cout << last << endl;
}
}
int main(){
int T;
cin >> T;
while(T--)
solve();
return 0;
}
给定无向图,点有点权 h i h_i hi,边有边权 x i x_i xi。每次询问 ( v , x , k ) (v,x,k) (v,x,k),从 v v v出发,只经过边权小于 x x x的边,能够到达的点中点权第 k k k大的点权。
只经过边权小于 x x x的边可以想到 K r u s k a l Kruskal Kruskal重构树,对于询问点权第 k k k大的点权可以想到主席树。
K r u s k a l Kruskal Kruskal重构树上非叶子结点"管辖"的叶子结点是一段连续的区间,在 d f s dfs dfs的时候,维护每个结点"管辖"的叶子结点区间。
对于询问 ( v , x , k ) (v,x,k) (v,x,k),与上题类似,利用倍增找到结点 u u u,在 u u u管辖的区间中,查询区间第 k k k大。注意离散化
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const int maxm = 5e5+10;
const int INF = 0x3f3f3f3f;
int n, m, q;
int h[maxn];
struct node{ //原图上边
int x, y, z;
bool operator < (const node &b)const{
return z < b.z;
}
}edg[maxm];
struct edge{ //重构树上边
int to, nxt;
}e[maxn];
int head[maxn], tot;
void add(int a, int b){
e[++tot].nxt = head[a];
e[tot].to = b;
head[a] = tot;
}
struct discreteH{ //高度离散化
int h, id;
bool operator < (const discreteH &b) const{
return h < b.h;
}
}dish[maxn];
int fa[maxn];
int val[maxn<<2]; //重构树上点权
int find(int x){
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
int nodetot;
void Kruskal(){
for(int i = 1; i <= n; i++)
fa[i] = i;
nodetot = n;
for(int i = 1; i <= m; i++){
int fx = find(edg[i].x);
int fy = find(edg[i].y);
if(fx == fy)
continue;
val[++nodetot] = edg[i].z;
fa[nodetot] = fa[fx] = fa[fy] = nodetot;
add(nodetot, fx); add(nodetot, fy);
}
}
int L[maxn], R[maxn], p[maxn][20], b[maxn];
int dfstot;
void dfs(int u, int fath){
p[u][0] = fath;
for(int i = 1; i <= 19; i++)
p[u][i] = p[p[u][i-1]][i-1];
L[u] = dfstot;
if(head[u] == 0){ //叶子即原图上的点
b[++dfstot] = u;
R[u] = dfstot;
return;
}
for(int i = head[u]; i; i = e[i].nxt){
dfs(e[i].to, u);
}
R[u] = dfstot;
}
int roottot;
struct tree{
int l, r, val;
}t[maxn*40];
int root[maxn];
void pushup(int rt){
t[rt].val = t[t[rt].l].val + t[t[rt].r].val;
}
void build(int &rt, int l, int r){
rt = ++roottot;
if(l == r)
return;
int mid = (l+r) >> 1;
build(t[rt].l, l, mid);
build(t[rt].r, mid+1, r);
}
void modify(int &rt, int pre, int l, int r, int pos){
rt = ++roottot;
t[rt] = t[pre];
if(l == r){
t[rt].val ++;
return;
}
int mid = (l+r) >> 1;
if(pos <= mid)
modify(t[rt].l, t[pre].l, l, mid, pos);
else
modify(t[rt].r, t[pre].r, mid+1, r, pos);
pushup(rt);
return;
}
int find_node(int u, int x){
int v = u;
for(int i = 19; i >= 0; i--)
if(val[p[v][i]] <= x)
v = p[v][i];
return v;
}
int query(int rt1, int rt2, int l, int r, int k){
if(l == r){
if(k == (t[rt1].val - t[rt2].val))
return l;
else
return 0;
}
int mid = (l+r) >> 1;
int d = t[t[rt1].r].val - t[t[rt2].r].val;
if(k <= d)
return query(t[rt1].r, t[rt2].r, mid+1, r, k);
else
return query(t[rt1].l, t[rt2].l, l, mid, k-d);
}
int main(){
cin >> n >> m >> q;
for(int i = 1; i <= n; i++){
cin >> dish[i].h;
dish[i].id = i;;
}
sort(dish+1, dish+n+1);
h[0] = INF;
for(int i = 1; i <= n; i++)
h[dish[i].id] = i;
for(int i = 1; i <= m; i++)
cin >> edg[i].x >> edg[i].y >> edg[i].z;
sort(edg+1, edg+1+m);
Kruskal();
dfs(nodetot, nodetot);
build(root[0], 1, n);
for(int i = 1; i <= dfstot; i++)
modify(root[i], root[i-1], 1, n, h[b[i]]);
dish[0].h = -1;
for(int i = 1; i <= q; i++){
int v, x, k;
cin >> v >> x >> k;
int ance = find_node(v, x);
int ans = query(root[R[ance]], root[L[ance]], 1, n, k);
cout << dish[ans].h << endl;
}
return 0;
}