题意:有n个点位于一平面上,现给出m条关系(给出的关系有时间上的顺序,每秒给一条)u v w ch 。(ch是方向)表示v在u的 东/南/西/北 w米。再给出k次查询,每次查询给出两个点a,b,以及一个时间t,表示在时间t的时候查询a到b的距离并输出。查询不到,就输-1。(两点之间的距离是曼哈顿距离)
思路:带权并查集,两个权值,分别记录子节点到根节点的水平方向的距离和竖直方向的距离。因为查询的时间有可能在合并的时间前面,所以要先记下所有的数据!(具体操作看代码注释)最后的查询操作是借鉴 奚政大佬 的
#include<iostream> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int Max=40005; struct node//记录点的信息,只记录东,和南,反方向为负即可 { int par,east,south; }; node p[Max]; int n,m,T; int u[Max],v[Max],w[Max];//分别储存输入的数据 char pos[Max]; int ans[Max];//记录答案 struct que{//记录查询信息index记录下标,t记录查询的时间 int index,a,b,t; bool operator < (const que a)const{ return t<a.t; } }q[40005]; void init()//初始化 { T=1; for(int i=1;i<=n;i++){ p[i].par=i; p[i].east=p[i].south=0; } } int Find(int x)//路径压缩,将每个子节点连到根节点上,压缩后子节点的距离记录的是到根节点的距离,而不是到父亲节点的距离 { if(x==p[x].par) return x; int tmp=p[x].par; p[x].par=Find(tmp); p[x].east+=p[tmp].east;//子节点到父亲节点的距离加上父亲节点到根节点的距离等于子节点到根节点的距离 p[x].south+=p[tmp].south; return p[x].par; } void unite(int x,int y,int w,char px)//合并 { int root1=Find(x); int root2=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(root1!=root2){//根节点不同才需要合并集合 p[root2].par=root1; p[root2].east=p[x].east-e-p[y].east;//合并集合后,距离的变化关系,拿题目给的图,随意选几个点就可以推出来 p[root2].south=p[x].south-s-p[y].south; } } void check(int t)//检查函数判断是否可以查询得到距离 { int x=q[t].a; int y=q[t].b; int root1=Find(x); int root2=Find(y); if(root1!=root2)//如果根不同,说明两个点并没有连在一起,查询不到 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() { cin>>n>>m; char ch; init(); for(int i=1;i<=m;i++) cin>>u[i]>>v[i]>>w[i]>>pos[i]; int k; cin>>k; for(int i=1;i<=k;++i){ cin>>q[i].a>>q[i].b>>q[i].t; q[i].index=i; } sort(q+1,q+k+1);//按查询时给出的时间排一下序,方便查询 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++) cout<<ans[i]<<endl; return 0; }