您的位置:首页 > 其它

BZOJ 1202: [HNOI2005]狡猾的商人(并查集)

2017-06-13 15:56 337 查看

Description

刁姹接到一个任务,为税务部门调查一位商人的账本,看看账本是不是伪造的。账本上记录了n个月以来的收入情况,其中第i 个月的收入额为Ai(i=1,2,3…n-1,n), 。当 Ai大于0时表示这个月盈利Ai 元,当 Ai小于0时表示这个月亏损Ai 元。所谓一段时间内的总收入,就是这段时间内每个月的收入额的总和。 刁姹的任务是秘密进行的,为了调查商人的账本,她只好跑到商人那里打工。她趁商人不在时去偷看账本,可是她无法将账本偷出来,每次偷看账本时她都只能看某段时间内账本上记录的收入情况,并且她只能记住这段时间内的总收入。 现在,刁姹总共偷看了m次账本,当然也就记住了m段时间内的总收入,你的任务是根据记住的这些信息来判断账本是不是假的。

Input

第一行为一个正整数w,其中w < 100,表示有w组数据,即w个账本,需要你判断。每组数据的第一行为两个正整数n和m,其中n < 100,m < 1000,分别表示对应的账本记录了多少个月的收入情况以及偷看了多少次账本。接下来的m行表示刁姹偷看m次账本后记住的m条信息,每条信息占一行,有三个整数s,t和v,表示从第s个月到第t个月(包含第t个月)的总收入为v,这里假设s总是小于等于t。

Output

包含w行,每行是true或false,其中第i行为true当且仅当第i组数据,即第i个账本不是假的;第i行为false当且仅当第i组数据,即第i个账本是假的。

Sample Input

2

3 3

1 2 10

1 3 -5

3 3 -15

5 3

1 5 100

3 5 50

1 2 51

Sample Output

true

false

Solution

这题不是差分约束,也不是2-SAT,就是个普通的并查集

对于每个读进来的区间[s,t],将其两个端点合并在一起。因为我们发现有冲突只可能一个区间的和为a,而该区间由其子区间表示时,b1+b2+..=b,而a不等于b。

于是我们想着维护前缀和,于是这就成了一个带权并查集。算出每个点i与其连通分量的根差作为sum[i],合并时顺带维护一下sum就行了。判断如果t与s−1在一个集合且sum[t]−sum[s−1]=v就代表可以,如果一旦不等就是假的账本。否则就合并。至于如何维护sum[]就不用说了吧。

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 150

using namespace std;

int w, n, m;
int bcj
, sum
;

int find_r(int x){
if(x == bcj[x])  <
e5ea
span class="hljs-keyword">return x;
int temp = bcj[x];
bcj[x] = find_r(bcj[x]);
sum[x] += sum[temp];
return bcj[x];
}

int main(){

scanf("%d", &w);
while(w --){
scanf("%d%d", &n, &m);

for(int i = 0; i <= n; i++)  bcj[i] = i, sum[i] = 0;

bool over = false;
int s, t, v;
for(int i = 1; i <= m; i++){
scanf("%d%d%d", &s, &t, &v);
s --;
int x = find_r(s), y = find_r(t);
if(x != y){
bcj[y] = x;
sum[y] = v + sum[s] - sum[t];
}
else if(sum[t] - sum[s] != v)  over = true;
}
if(!over)  printf("true\n");
else  printf("false\n");
}

return 0;
}




梦里不觉秋已深,余情岂是为他人。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: