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;
}

本文介绍了一种使用并查集解决区间和问题的方法,通过将区间和转换为点之间的差值来简化处理过程。这种方法适用于需要判断多个区间和是否一致的问题场景。
626

被折叠的 条评论
为什么被折叠?



