如题:http://poj.org/problem?id=1984
很有意思的一题。题目一开始没看懂,搞了一下午。
题目要求有n个农场,m条路。输入每条路的起点,终点,距离,和终点相对于起点的方向(东南西北)。
然后给出k组问题,每一个而难题给出一个起点,终点和一个I,要求只使用前面输入路时第I条路之前的数据能否求出起点,终点的Manhattan距离|x1-x2|+|y1+y2|。
2个数组,s1,s2存放相对于集合根元素的水平和垂直偏移量。
1.如果能求出2点距离,一定是在一个集合。
2.在查找父节点路径压缩的同时,偏移量就会递归更新,最后父节点更改完,偏移量s1,s2也完成修改。
3.默认为’N,E‘方向为+,‘W,S’方向为-。在合并时会用到。
#include<cstdio>
#include<algorithm>
struct Road
{
int begin,end,len;
char dir;
}r[40010];
struct Query
{
int begin,end,id;
int num;
}q[10010];
int f[40010],s1[40010],s2[40010],ans[10010];//s1为水平,s2为竖直
int n,m;
int cmp(const void * a,const void * b)
{
return (*(Query *)a).id-(*(Query *)b).id;
}
int find(int x)
{
if(x==f[x])
return x;
int f1=f[x];
f[x]=find(f[x]);
s1[x]+=s1[f1]; //注意偏移量的更新,递归完成。
s2[x]+=s2[f1];
return f[x];
}
void Union(int x)
{
int fb=find(r[x].begin);
int fe=find(r[x].end);
if(fb!=fe)
{
f[fb]=fe;
if(r[x].dir=='E')
{
s1[fb]=s1[r[x].end]-s1[r[x].begin]+r[x].len;
s2[fb]=s2[r[x].end]-s2[r[x].begin];
}
else if(r[x].dir=='N')
{
s1[fb]=s1[r[x].end]-s1[r[x].begin];
s2[fb]=s2[r[x].end]-s2[r[x].begin]+r[x].len;
}
else if(r[x].dir=='W')
{
s1[fb]=s1[r[x].end]-s1[r[x].begin]-r[x].len;
s2[fb]=s2[r[x].end]-s2[r[x].begin];
}
else
{
s1[fb]=s1[r[x].end]-s1[r[x].begin];
s2[fb]=s2[r[x].end]-s2[r[x].begin]-r[x].len;
}
}
}
int main()
{
// freopen("C:\\1.txt","r",stdin);
scanf("%d%d",&n,&m);
int i;
for(i=1;i<=n;i++)
{
f[i]=i;
s1[i]=s2[i]=0;
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&r[i].begin,&r[i].end,&r[i].len);
getchar();
scanf("%c",&r[i].dir);
}
int nq=0;
scanf("%d",&nq);
for(i=1;i<=nq;i++)
{
scanf("%d%d%d",&q[i].begin,&q[i].end,&q[i].id);
q[i].num=i;
}
qsort(q+1,nq,sizeof(q[0]),cmp);
int curq=1;
for(i=1;i<=m;i++)//双重循环+排序使得所有查询和排序按顺序进行。每次合并一个,判断查询id有没有等于当前i的。
{
Union(i);
while(q[curq].id==i&&curq<=nq)
{
int fb=find(q[curq].begin);
int fe=find(q[curq].end);
if(fb==fe)
ans[q[curq].num]=abs(s1[q[curq].begin]-s1[q[curq].end])+abs(s2[q[curq].begin]-s2[q[curq].end]);
else
ans[q[curq].num]=-1;
curq++;
}
if(curq==nq+1)
break;
}
for(i=1;i<=nq;i++)
printf("%d\n",ans[i]);
return 0;
}