题目链接:http://poj.org/problem?id=1984
题目大意,一开始给出一个n和m,分别代表一共有n个节点,m次操作,每次操作花费一秒,按照顺序执行。然后有k次询问,询问有三个变量f1,f2,i,问f1和f2的哈密顿距离是多少。哈密顿距离就是两个点坐标各自相减取绝对值,|x1 - x2|+ |y1 - y2|。样例就是题目上面那个图。i是询问第几秒时候他们是否联通,若联通则输出距离。
这个题目,我从星期一开始做,做到今天星期五终于做出来了,好久没有试过磨出来一道题了,习惯看题解了,这次我觉得我能做就一直没看题解。后来我也真的是前前后后wa了20多次的样子,还找到了他的测试数据,发现自己好多东西都不对。一直改一直改,今天晚上,重新审视一下我的代码,突然灵光一现,重新制定了计划,去掉了很多东西,就过了,还不错。
说说我最后的想法,修改多次了!!!
这个题目有东南西北,我们只需要x和y去存就行了,读入到E就是+x的方向嘛,N就是+y的方向嘛。带权并查集订好了距离其实本质就是并查集。
并查集就不多讲了,然后现在就是要想怎么把距离也更新。
1,如果有这样一个关系,a和b要合并,他们的根分别是c和d,那么我永远都规定后面那个根连到前面那个根上面。但是根,本来他的x和y肯定是(0,0)的,那么我先从d走返回去b那,说白了就是d.x += -b.x , d.y += -b.y,这时候,我要连接到c上面,那么我先根据他们之间的关系,NSEW什么的,又再次加到d.x , d.y上面,这时候我们知道a.x, a.y,再把这个加到d.x, d.y上面,完美,这不就是d到c的距离更新完了吗?然后再来一句,par[d] = c,搞定。
2,这样据说是可以过的,但是我觉得还不够,我决定还要路径压缩一下,这里的路径压缩,有点意思,我也是想了好几种方案才相出这种递归压缩的方法。跟以前的不同的就是,比如a连着b,b连着c,c连着d,d是根,我们要a连接到d上,应该是从a一直循环下去,记住,必须是循环。a.x += b.x, a.y += b.y,然后a.x += c.x, a.y += c.y,这样我们就把a直接连接到d上面了。接下来,用一个递归,par[a] = Find(par[a]),这样一进去,又执行b的更新操作,Find函数的返回值是查找元素的根。
3,我们要看看这个查询时候的时间问题了,我们不可能把每一时刻每个点的状态都存下来,否则就要有5W * 5W的复杂度了。明智的做法就是,把这些数据全部先存起来,然后再一边建图一边查询。
代码如下:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<math.h>
#include<stdlib.h>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 40000 + 5;
int par[40005], disx[40005], disy[40005];
int a[MAXN], b[MAXN], v[MAXN], str[MAXN];
struct node
{
char ch;
int point, val;
};
struct ti
{
int fa,fb,t,id;
int val;
}f[MAXN];
bool cmp(ti a, ti b)
{
return a.t < b.t;
}
bool cmp2(ti a, ti b)
{
return a.id < b.id;
}
void init()
{
memset(disx, 0, sizeof(disx));
memset(disy, 0, sizeof(disy));
for(int i = 0; i < 40005; i++)
par[i] = i;
}
int Find(int x)
{
if(par[x] == x)
return x;
int p = x;
while(par[p] != p)
{
disx[x] += disx[par[p]];
disy[x] += disy[par[p]];
p = par[p];
}
par[x] = Find(par[x]);
return par[x];
}
int Find_dis(int a, int b)
{
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
int x;
x = a;
while(par[x] != x)
{
x1 += disx[x];
y1 += disy[x];
x = par[x];
}
x = b;
while(par[x] != x)
{
x2 += disx[x];
y2 += disy[x];
x = par[x];
}
return abs(x1 - x2) + abs(y1 - y2);
}
void Union(int a, int b, int x, int y, int v, char ch)
{
node tmp;
disx[y] = -disx[b] + disx[a];
disy[y] = -disy[b] + disy[a];
if(ch == 'N')
disy[y] -= v;
else if(ch == 'E')
disx[y] -= v;
else if(ch == 'S')
disy[y] += v;
else if(ch == 'W')
disx[y] += v;
par[y] = x;
}
int main()
{
char ch;
int n, m, ppp, k;
while(cin >> n >> m)
{
init();
ppp = 1;
for(int i = 1; i <= m; i++)
scanf("%d %d %d %c", &a[i], &b[i], &v[i], &str[i]);
cin >> k;
for(int i = 1; i <= k; i++)
{
scanf("%d%d%d", &f[i].fa, &f[i].fb, &f[i].t);
f[i].id = i;
}
sort(f + 1, f + k + 1, cmp);
for(int i = 1; i <= m; i++)
{
int x = Find(a[i]);
int y = Find(b[i]);
if(x != y)
Union(a[i], b[i], x, y, v[i], str[i]);
while(ppp <= k && f[ppp].t == i)
{
if(Find(f[ppp].fb) == Find(f[ppp].fa))
f[ppp].val = Find_dis(f[ppp].fa, f[ppp].fb);
else
f[ppp].val = -1;
ppp++;
}
}
sort(f + 1, f + k + 1, cmp2);
for(int i = 1; i <= k; i++)
printf("%d\n", f[i].val);
}
return 0;
}