HDU-3038 How Many Answers Are Wrong(带权并查集区间合并)

http://acm.hdu.edu.cn/showproblem.php?pid=3038

大致题意:

有一个区间[0,n],然后会给出你m个区间和,每次给出a,b,v,表示区间[a,b]的区间和为v,但每次给出的区间可能与之前的有冲突,问这样起冲突的区间共有多少个

首先区间[a,b]的和可由区间[0,b]的和减去区间[0,a-1]的和得到

但是我们不太可能知道[0,b],故我们只用知道和b的合并过的区间的左端点就行


其实并查集实质就是一颗树,我们可以以树的角度去看待它,理解维护过程

不理解的同学可以在纸上多划几遍,理解过程

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <iostream>
 4 #include <string>
 5 #include <math.h>
 6 #include <algorithm>
 7 #include <vector>
 8 #include <stack>
 9 #include <queue>
10 #include <set>
11 #include <map>
12 #include <sstream>
13 const int INF=0x3f3f3f3f;
14 typedef long long LL;
15 const int mod=1e9+7;
16 const int maxn=1e5+10;
17 using namespace std;
18 
19 int fa[200005];//fa[i] 存i的最左端点
20 int sum[200005];//记录i到根节点之间和的差值,若再跟节点右侧 则为负数
21 
22 void init(int n)
23 {
24     for(int i=0;i<=n;i++)
25         fa[i]=i;
26 }
27 int Find(int x)//带权并查集,此时find不单有查找任务,还有更新距离任务
28 {
29     if(x!=fa[x])
30     {
31         int t=fa[x];//记录之前的父节点
32         fa[x]=Find(fa[x]);//路径压缩 
33         sum[x]+=sum[t];//递归维护sum
34     }//记录到根节点的距离,根节点是一个区间的一个端点而不是一个区间,输入的区间被合并成了两个点
35     return fa[x];
36 }
37 
38 int main()
39 {
40     #ifdef DEBUG
41     freopen("sample.txt","r",stdin);
42     #endif
43 //    ios_base::sync_with_stdio(false);
44 //    cin.tie(NULL);
45     
46     int n,m;
47     while(~scanf("%d %d",&n,&m)) 
48     {
49         init(n);
50         memset(sum,0,sizeof(sum));
51         int ans=0;
52         for(int i=1;i<=m;i++)
53         {
54             int l,r,v;
55             scanf("%d %d %d",&l,&r,&v);//
56             int x=Find(l-1); //注意为什么要-1
57             int y=Find(r);
58             if(x!=y)//如果不是一个集合 合并 
59             {
60                 fa[y]=x;
61                 sum[y]=sum[l-1]-sum[r]+v;
62             }
63             else if(sum[r]-sum[l-1]!=v) ans++;//当有相同的最左端时,直接判断
64         }
65         printf("%d
",ans);
66     }
67     
68     return 0;
69 }

-

原文地址:https://www.cnblogs.com/jiamian/p/12256619.html