[NOI2018]归程

神奇的kruskal重构树(考场不会可以现场发明)。。。

首先跑一遍dj,则对于每次询问,答案就是v能到的所有点中dis最小的点的dis

考虑按海拔高度,从大到小加边直到图联通,我们需要维护每个点所在的连通块,以及每个连通块中dis最小的点

离线。。。并查集?

强制在线。。。可持久化并查集?

有没有更优秀的做法?我们可以用kruskal重构树。

对于加入的一条边(u,v),我们找到u和v所在树的根节点x和y,新建一个节点new并把x和y的父亲设为new,new的点权则是新加入边的边权

这样一个点u走海拔超过d的边能到的点就是其经过点权超过d的点能到的点,我们发现父亲的点权总是小于儿子,因此我们倍增找到u能到的深度最浅的祖先v,u能到的所有点就是v的子树

#include<bits/stdc++.h>
#define file(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define P 998244353
#define mid (l+r>>1)
#define N 1100000
#define lb(x) (x&(-x))
#define inf 999999999
#define M 1658561
#define ll long long
#define mem(x) memset(x,0,sizeof(x));
using namespace std;
int dis[N],f[N][21],ls,cnt,tot,n,m,T,Q,k,s,fa[N],to[N],nxt[N],head[N],w[N],mi[N],vis[N],p,d[N],v;
struct edge{int x,y,w,d;}e[N];
struct node{
    int id,dis;
    friend bool operator <(node x,node y){return x.dis>y.dis;}
};
priority_queue<node>q;
bool cmp(edge x,edge y){ return x.d>y.d;}
int find(int x){ return fa[x]==x? x:fa[x]=find(fa[x]);}
void add(int x,int y,int z){
//    printf("%d %d %d\n",x,y,z);
    to[++cnt]=y;
    nxt[cnt]=head[x];
    head[x]=cnt;
    w[cnt]=z;
}
int dfs2(int x){
//    printf("%d %d\n",x,dis[x]);
    mi[x]=dis[x];
    for(int i=head[x];i;i=nxt[i])mi[x]=min(mi[x],dfs2(to[i]));
    return mi[x];
}
void dj(){
    memset(dis,0x3f,sizeof(dis));
    mem(vis);
    q.push({1,0});dis[1]=0;
    while(!q.empty()){
        int x = q.top().id;q.pop();
        if(vis[x]) continue; vis[x] = 1;
        for(int i=head[x];i;i=nxt[i]){
            if(dis[to[i]]>dis[x]+w[i]){
                dis[to[i]]=dis[x]+w[i];
                q.push({to[i],dis[to[i]]});
            }
        }
    }
}
int main(){
    //file("s");
    scanf("%d",&T);
    while(T--){
        mem(head);mem(d);ls=cnt=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++) scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].w,&e[i].d),add(e[i].x,e[i].y,e[i].w),add(e[i].y,e[i].x,e[i].w);
        dj();
        mem(head);cnt=0;
        scanf("%d%d%d",&Q,&k,&s);
        sort(e+1,e+m+1,cmp);tot=n;
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m&&tot-n<n-1;i++){
            int x=find(e[i].x),y=find(e[i].y);
    //        printf("/%d %d/\n",x,y);
            if(x==y) continue;
    //        printf("-%d %d-\n",x,y);
            d[++tot]=e[i].d;
            fa[tot]=tot;
            add(tot,x,0);add(tot,y,0);
            fa[x]=f[x][0]=fa[y]=f[y][0]=tot;
        }
        for(int i=1;i<=20;i++) for(int j=1;j<=tot;j++) f[j][i]=f[f[j][i-1]][i-1];
        //printf("%d\n",tot);
        dfs2(tot);
        //return 0;
        for(int i=1;i<=Q;i++){
            scanf("%d%d",&v,&p);
            v=(v+k*ls-1)%n+1;p=(p+k*ls)%(s+1);
            for(int i=20;~i;i--) if(d[f[v][i]]>p)v=f[v][i];
            printf("%d\n",ls=mi[v]);
        }
    }
    return 0;
}

 

posted on 2020-07-06 21:14  啊啊啊起个什么名字好  阅读(154)  评论(0编辑  收藏  举报

导航