HDU_3038 How Many Answers Are Wrong 【带权并查集】

一、题面

HDU3038

二、分析

用并查集可以方便的判断两个位置是否有关系,这种关系可以通过是否有公共父节点判断,如果有公共父节点则可以直接判断是否正确,如果没有公共父节点,就可以把这个条件与之前的联系起来。同时需要设定sum,表示当前点到父节点的权值,这个权值方便后面的判断,这里有几种情况。

假设输入为$(x,y,w)$,那么对应父节点$fx = find(x), fy = find(y)$:

1.如果$fx==fy$:那么可以直接判断$w == sum[x] - sum[y]$.

2.如果$ x < y < fx < fy$:这里需要更新并查集的信息,$par[fx] = fy, sum[fx] = sum[y] - (sum[x] - w)$.

3.如果$ x < fx < y < fy$:此时,$par[fx] = fy, sum[fx] = sum[y] + (w - sum[x])$.

4.如果$ x < y < fy < fx$:此时,$par[fy] = fx, sum[fy] = sum[x] - sum[y] - w$.

分析上面四种情况,可以得出2和3可以合并,对于4,仔细分析一下, 相当于就是3的情况,只是因为$par[fy] = fx$,只是这样可以保证$sum$的值为正。注意在路径压缩时,对$sum$进行更新就可以了。

细心的朋友可能发现了,还有$x = y$的情况呢。对于这种情况,我们可以直接对输入的左值整体再往左移一下,就是输入的$u$变成$u-1$,这个对结果是没有影响的。但是如果不减,我们就要考虑$x=y$的情况了,前面我们已经初始化了$sum$初值为0,并且$x=y$的左右两边的情况我们是不能保证完全清楚的。所以这样把$x=y$变成了$(x-1,y]$这个区间的形式,就让问题又归到了我们上面讨论的情况,更简单了。

三、AC代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <fstream>
 5 #include <map>
 6 
 7 using namespace std;
 8 
 9 const int MAXN = 2e5+3;
10 int par[MAXN];
11 int sum[MAXN];
12 
13 void Init()
14 {
15     for(int i = 0; i < MAXN; i++)
16         par[i] = i;
17     memset(sum, 0, sizeof(sum));
18 }
19 
20 int Find(int x)
21 {
22     if(par[x] == x)
23         return x;
24     int fx = par[x];
25     par[x] = Find(fx);
26     sum[x] = sum[fx] + sum[x];
27     return par[x];
28 }
29 
30 bool Union(int x, int y, int w)
31 {
32     int fx = Find(x);
33     int fy = Find(y);
34     // if(fx != fy)
35     // {
36     //     par[fx] = fy;
37     //     sum[fx] = w + sum[y] - sum[x];
38     //     return true;
39     // }
40     // else
41     // {
42     //     return w == sum[x] - sum[y];
43     // }
44     if(fx < fy)
45     {
46         par[fx] = fy;
47         sum[fx] = w + sum[y] - sum[x];
48         return true;
49     }
50     else if(fx > fy)
51     {
52         par[fy] = fx;
53         sum[fy] = sum[x] - w - sum[y];
54         return true;
55     }
56     else
57     {
58         return w == sum[x] - sum[y];
59     }
60 
61 }
62 
63 int main()
64 {
65    // freopen("input.txt", "r", stdin);
66     int N, M;
67     while(scanf("%d %d", &N, &M)!=EOF)
68     {
69         Init();
70         int a, b, w, ans = 0;
71         for(int i = 0; i < M; i++)
72         {
73             scanf("%d %d %d", &a, &b, &w);
74             if(!Union(a-1, b, w))
75                 ans++;
76         }
77         printf("%d\n", ans);
78     }
79     return 0;
80 }
View Code

 

转载于:https://www.cnblogs.com/dybala21/p/10138765.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值