P4768 [NOI2018]归程(可持久化并查集+最短路)

传送门

题意:给你一张图,图中有m条边,每条边有长度和一个海拔值,给你q次询问,每次给你一个节点v和一个海拔值p,问你从v到1的最短距离是多少,对于图中的边如果海拔值小于p,那边的长度在这次查询时就会变为0;

题解:首先对于海拔大于o,那边连接的点就相当于一个点,那对于一个联通块来说,联通块中的所有点到1的最短距离就是就是最短距离对应的那个点,所以我们需要维护一个并查集,然后考虑对于每次海拔的更新,我们所对应的边也会被更新。所以考虑以海拔值做时间,海拔值相同的边对应的时间相同,我们每次询问是只需要访问p对应的当前版本中包含v的联通块到1的最小距离就好了。对于主席树维护的持久化并查集在合并父亲节点时需要按秩归并,而不能用最短路径当做主席树的秩。至于最短路用dij求出,SPFA已经

AC代码:

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
#include<vector>
#include<iostream>
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int maxn=2e5+5;
const int inf=0x3f3f3f3f;
struct edge
{
    int next, ed, len;
};
edge e[maxn*20];
int p[maxn], cnt;
void add_edge(int st,int ed,int len)
{
    e[cnt].ed=ed;
    e[cnt].next=p[st];
    e[cnt].len=len;
    p[st]=cnt++;
}
int read(){
    int x=0,w=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')w=0,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
struct qnode{
    int v,c;
    qnode(int _v=0,int _c=0):v(_v),c(_c){}
    bool operator < (const qnode &r) const {
        return c>r.c;
    }
};
bool vis[maxn];
int dist[maxn],father[maxn];
priority_queue<qnode> que;
void dijkstra(int n){
    memset(vis,false,sizeof(vis));
    for(int a=1;a<=n;a++)
        dist[a]=inf,father[a]=a;
    dist[1]=0;
    que.push(qnode{1,0});
    qnode tmp;
    while(!que.empty()){
        tmp=que.top();
        que.pop();
        int u=tmp.v;
         if(vis[u])
            continue;
        vis[u]=true;
        for(int a=p[u];a!=-1;a=e[a].next){
            int v=e[a].ed;
            int cost=e[a].len;
            if(!vis[v]&&dist[v]>dist[u]+cost){
                dist[v]=dist[u]+cost;
                que.push(qnode(v,dist[v]));
            }
        }
    }
}
struct node{
    int lson,rson,fa;
    int level;
};
node tree[maxn*40];
struct node1{
    int u,v;
    int a;
};
node1 side[2*maxn];
bool cmp(node1 a,node1 b){
    if(a.a<b.a)
        return true;
    return false;
}
int sea[maxn*2];
int tj,mn[maxn*40];
int ti[maxn*2];
int find1(int x){
    if(x==father[x])
        return x;
    return father[x]=find1(father[x]);
}
void build(int &now,int l,int r){
    now=++tj;
    if(l==r){
        tree[now].fa=l;
        tree[now].level=dist[l];
        mn[now]=dist[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(tree[now].lson,l,mid);
    build(tree[now].rson,mid+1,r);
}
int getfa(int now1,int x,int n){
    int now;
    int l,r;
    while(1){
        l=1,r=n;
        now=now1;
        while(l!=r){
        int mid=(l+r)>>1;
        if(mid>=x){
            r=mid;
            now=tree[now].lson;
        }
        else{
            l=mid+1;
            now=tree[now].rson;
        }
        }
        if(x==tree[now].fa)
            return now;
        x=tree[now].fa;
    }
}
int add(int *now,int lst,int l,int r,int x){
    while(l!=r){
        *now=++tj;
        int mid=(l+r)>>1;
        tree[*now].lson=tree[lst].lson;
        tree[*now].rson=tree[lst].rson;
        if(x<=mid){
            lst=tree[lst].lson;
            r=mid;
            now=&(tree[*now].lson);
        }
       else{
            lst=tree[lst].rson;
            l=mid+1;
            now=&(tree[*now].rson);
        }
    }
    return *now=++tj;
}
inline void work(int now,int x,int y,int n){
    int fx=find1(x),fy=find1(y);
    if(fx==fy)
        return ;
    fx=getfa(ti[now],x,n),fy=getfa(ti[now],y,n);
    if(tree[fx].level>tree[fy].level)
        swap(fx,fy);
    father[tree[fx].fa]=tree[fy].fa;
    tree[add(&ti[now],ti[now],1,n,tree[fx].fa)].fa=tree[fy].fa;
    int w=add(&ti[now],ti[now],1,n,tree[fy].fa);
    tree[w].fa=tree[fy].fa;
    mn[w]=min(mn[fx],mn[fy]);
    tree[w].level=tree[fy].level+(tree[fx].level==tree[fx].level);
    //printf("*\n");
    //printf("%d\n",getfa(ti[now],3,n));
}
int main( )
{
    int t,n,m;
    t=read();
    while(t--){
        n=read(),m=read();
        int u,v,l,a;
        cnt=0;
        memset(p,-1,sizeof(p));
        for(int i=1;i<=m;i++){
                u=read(),v=read(),l=read(),a=read();
                add_edge(u,v,l);
                add_edge(v,u,l);
                side[i]={u,v,a};
                sea[i]=a;
        }
        dijkstra(n);
        int q,k1,s,v1,p;
        q=read(),k1=read(),s=read();
        sort(side+1,side+m+1,cmp);
        for(int i=1;i<=m;i++)
            sea[i]=side[i].a;
            sea[m+1]=s+1;
        int num=unique(sea+1,sea+m+2)-sea-1;
        tj=0;
        build(ti[num],1,n);
        for(int i=num-1,j=m;i;i--){
            ti[i]=ti[i+1];
            for(;j&&side[j].a==sea[i];j--){
                work(i,side[j].u,side[j].v,n);
            }
        }
        int lastans=0;
        while(q--){
            v1=(read()+k1*lastans-1)%n+1;
            p=(read()+k1*lastans)%(s+1);
            //printf("%d %d %d\n",v1,p,upper_bound(sea+1,sea+num+1,p));
            lastans=mn[getfa(ti[upper_bound(sea+1,sea+num+1,p)-sea],v1,n)];
            printf("%d\n",lastans);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值