HDU-3038 How Many Answers Are Wrong(带权并查集区间合并)
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\n",ans); 66 } 67 68 return 0; 69 }
-