poj1984 带权并查集

 

 

 

如题: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;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值