POJ - 1984 Navigation Nightmare 带权并查集

参考博客:https://blog.csdn.net/qq_41874469/article/details/80329447

题意:有n个点位于一平面上,现给出m条关系(给出的关系有时间上的顺序,每秒给一条)u  v  w  ch  。(ch是方向)表示v在u的  东/南/西/北 w米。再给出k次查询,每次查询给出两个点a,b,以及一个时间t,表示在时间t的时候查询a到b的距离并输出。查询不到,就输-1。(两点之间的距离是曼哈顿距离)
     思路:带权并查集,两个权值,分别记录子节点到根节点的水平方向的距离和竖直方向的距离。因为查询的时间有可能在合并的时间前面,所以要先记下所有的数据!
 

#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn=4e4+5;

struct node{
    int fa,east,south;
}p[maxn];

int n,m,T;
int u[maxn],v[maxn],w[maxn];
char pos[maxn];

int ans[maxn];

struct Node{
    int a,b,t,index;
    bool operator < (const Node& f)const {
        return t<f.t;
    }
}q[maxn];

void init(){
    T=1;
    for(int i=1;i<=n;i++){
        p[i].fa=i;
        p[i].east=p[i].south=0;
    }
}

int find(int x){
    if(x==p[x].fa) return x;
    int tmp=p[x].fa;
    p[x].fa=find(tmp);
    p[x].east+=p[tmp].east;
    p[x].south+=p[tmp].south;
    return p[x].fa;
}

void unite(int x,int y,int w,char px){
    int r1=find(x);
    int r2=find(y);
    int e=0,s=0;
    if(px=='N') s=-w;
    else if(px=='S') s=w;
    else if(px=='W') e=-w;
    else e=w;
    if(r1!=r2){
        p[r2].fa=r1;
        p[r2].east=p[x].east-e-p[y].east;
        p[r2].south=p[x].south-s-p[y].south;
    }
}

void check(int t){
    int x=q[t].a;
    int y=q[t].b;
    int r1=find(x);
    int r2=find(y);
    if(r1!=r2){
        ans[q[t].index]=-1;
    }else{
        ans[q[t].index]=abs(p[x].east-p[y].east)+abs(p[x].south-p[y].south);
    }
    T++;
}

int main(){
    scanf("%d%d",&n,&m);
    init();
    for(int i=1;i<=m;i++){
        scanf("%d%d%d %c",&u[i],&v[i],&w[i],&pos[i]);
    }
    int k;
    scanf("%d",&k);
    for(int i=1;i<=k;i++){
        scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].t);
        q[i].index=i;
    }
    sort(q+1,q+1+k);
    for(int i=1;i<=m;i++){
        unite(u[i],v[i],w[i],pos[i]);
        while(q[T].t==i&&T<=k) check(T);
    }
    for(int i=1;i<=k;i++)
        printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40679299/article/details/83833345