bzoj1202 [HNOI2005]狡猾的商人 (带权并查集)

来源:互联网 发布:彩虹跳转源码 编辑:程序博客网 时间:2024/05/20 19:32

bzoj1202 [HNOI2005]狡猾的商人

原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=1202

题意:
T组数据。
账本上记录了n个月以来的收入情况,其中第i 个月的收入额为Ai(i=1,2,3…n-1,n)。当 Ai大于0时表示这个月盈利Ai 元,当 Ai小于0时表示这个月亏损Ai 元。所谓一段时间内的总收入,就是这段时间内每个月的收入额的总和。
给出m段时间内的总收入,每次有三个整数s,t和v,表示从第s个月到第t个月(包含第t个月)的总收入为v,这里假设s总是小于等于t。你的任务是这些信息来判断账本上的信息是否矛盾。
数据范围
T<=100,n < 100,m < 1000

题解:
第二次做这种用并查集维护区间信息的题。

一般给出一个区间的和,不好处理,常用的转化方式:
通过前缀和转化为差的形式。
例如,[l,r]和为w,即转化为sum[r]-sum[l-1]=w
差,既可加减,便于合并,又便于处理单点权值的情况(pos,pos-1不会把自己和自己加入并查集)。

对于这道题,考虑到便于合并,我们维护一个点x与fa[x]的前缀和之差d[x]=sum[fa[x]]-sum[x]。初始值为0。在getfa路径压缩时就可以一并把这个值推到根。
具体来说:
给出[l,r]区间值为v。
x=l-1; y=r;
另fx为x所在并查集的根,fy为y所在并查集的根。
sum[y]-sum[x]=v
sum[fy]-sum[y]=d[y]
sum[fx]-sum[x]=d[x]
若把fa[fy]指向fx,由上式代入得 d[fy]=d[x]-d[y]-v

最后每给出l,r,若l-1,r已经在一个并查集就看sum[r]-sum[l-1]=d[l-1]-d[r]是否等于v,否则像上面那样合并。

维护差值很巧妙,尤其是可以在路径压缩时一并推到根,也可以加减。

另外,像这样并查集维护区间也可以判一个区间是否能被其他点不重复的区间表示。

代码:

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>using namespace std;const int N=105;int T,n,m,fa[N],d[N];int getfa(int x){    if(x==fa[x]) return x;    else    {        int f=fa[x];        int root=getfa(fa[x]);        d[x]=d[x]+d[f];        return fa[x]=root;    } }int main(){    scanf("%d",&T);    while(T--)    {        scanf("%d%d",&n,&m);        for(int i=0;i<=n;i++) {fa[i]=i; d[i]=0;}        bool flag=0;        while(m--)        {            int x,y,v;            scanf("%d%d%d",&x,&y,&v);               x--;            int fx=getfa(x); int fy=getfa(y);            if(fx!=fy)            {                d[fy]=d[x]-d[y]-v;                fa[fy]=fx;            }            else if(d[x]-d[y]!=v) flag=1;        }        if(flag) printf("false\n");        else printf("true\n");    }    return 0;}
原创粉丝点击