题意:
给出n个农场,然后按时间依次给出m个关于农场相对位置的信息,之后会给出询问,问在t时刻,x到y的曼哈顿距离是多少。
题解:
dx【i】维护 根节点到 i 的横坐标距离 dy【i】维护 根节点到 i 的纵坐标距离
并查集高效的地方就在于在使用Find(x)函数查找x的父结点的时候会把沿途递归访问到的所有结点直接连到父节点上,使得下一次查询可以直接找到父节点,代码表示就是f[x]=Find(f[x]);
之前接触到的都是比较裸的并查集,但就像这题,维护并查集的时候还需要维护点到其父节点的距离(dx[x]+=dx[f[x]];),再构造Find(x)函数时就得同时考虑对距离的维护,维护方式一旦不对就有可能导致距离错误。
维护连通块内的结点到父节点的横坐标距离和纵坐标的距离,查询时先判断是否属于同一个集合内 若属于则将两点到父节点的横轴距离和纵轴距离相减求得曼哈顿距离即可
重点是如何进行集合合并
先让 fx指向fy 即 f[fy]=fx 然后分情况讨论
如果 d==W dx【fx】= dx【a[i].f2】 - dx【a[i].f1】- a【i】.l dy【fx】=dy【a[i].f2】-dy【a[i].f1】
如果 d==E dx【fx】= dx【a[i].f2】 - dx【a[i].f1】+ a【i】.l dy[fx]=dy【a[i].f2】-dy【a[i].f1】
如果 d==S dy【fx】= dy【a[i].f2】 - dy【a[i].f1】+ a【i】.l dx[fx]=dx【a[i].f2】-dx【a[i].f1】
如果 d==N dy【fx】= dy【a[i].f2】 - dy【a[i].f1】- a【i】.l dx[fx]=dx【a[i].f2】-dx【a[i].f1】
然后将上面四种情况合并
代码:
#include<iostream> #include<stdio.h> #include<math.h> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int maxn = 2e5+7; int f[maxn],dx[maxn],dy[maxn]; struct node { int f1,f2,l; char d; } a[maxn]; int n,m,k; int Find(int x) { if(f[x]==x)return x; int root=Find(f[x]); dx[x]+=dx[f[x]]; dy[x]+=dy[f[x]]; return f[x]=root; } int main() { scanf("%d%d",&n,&m); for(int i=0;i<=n;i++)f[i]=i; for(int i=1;i<=m;i++) { scanf("%d%d%d",&a[i].f1,&a[i].f2,&a[i].l); cin>>a[i].d; } scanf("%d",&k); int i=1; while(k--) { int F1,F2,L; scanf("%d%d%d",&F1,&F2,&L); while(i<=L) { int fx=Find(a[i].f1); int fy=Find(a[i].f2); if(fx!=fy) { f[fx]=fy; dx[fx]=dx[a[i].f2]-dx[a[i].f1]; dy[fx]=dy[a[i].f2]-dy[a[i].f1]; if(a[i].d=='W') dx[fx]-=a[i].l; else if(a[i].d=='E')dx[fx]+=a[i].l; else if(a[i].d=='S')dy[fx]+=a[i].l; else dy[fx]-=a[i].l; } i++; } int fx=Find(F1); int fy=Find(F2); if(fx!=fy)printf("-1\n"); else { int ans=abs(dx[F1]-dx[F2])+abs(dy[F1]-dy[F2]); printf("%d\n",ans); } } return 0; }View Code