HDU3038 How Many Answers Are Wrong

题目大意

%   给定 n n n m m m,对于 m m m 组形如l r d的信息,其中 1 ⩽ l ⩽ r ⩽ d 1\leqslant l\leqslant r\leqslant d 1lrd,含义为对于 A A A序列,有 ∑ i = l r a i = d \sum_{i=l}^ra_i=d i=lrai=d

%   如果有一条信息可能是正确的,我们认为它就是正确的,请输出有多少条信息是不正确的。
  数据范围  1 ⩽ n ⩽ 2 × 1 0 5 , 1 ⩽ m ⩽ 40000 1\leqslant n\leqslant 2\times 10^5,1\leqslant m\leqslant40000 1n2×105,1m40000

题解

%    由于维护区间问题实在困难,我们考虑前缀和,用 s u m [ i ] sum[i] sum[i] 表示 a j ( 1 ⩽ j ⩽ i ) a_j(1\leqslant j\leqslant i) aj(1ji) 的和。这样同样有一个问题,我们无法根据若干个区间简单方便地获得 s u m sum sum 数组的确切值,而一旦能获得确切值,说明已经求出了答案。
  我们考虑将关系建成一棵树,对每个下标建一个编号相同的点, f a [ u ] fa[u] fa[u] 表示点 u u u 的父亲,在这里,我们规定 f a [ u ] ⩽ u fa[u]\leqslant u fa[u]u,即总是让大的编号成为小的编号的儿子。
  如此,我们终于可以开始定义了,若点 u u u 的最远祖先为 F F F,定义 s u m [ u ] sum[u] sum[u] 表示 a i ( F ⩽ i ⩽ u ) a_i(F\leqslant i\leqslant u) ai(Fiu) 的和。
  当我们得知 ∑ [ l , r ] = d \sum[l,r]=d [l,r]=d 时,若 l l l r r r 在同一棵树内,则让 l l l r r r 更新其到最远祖先沿途的点的 s u m sum sum ,并且压缩路径。容易看出,这其实就是个带权并查集。
  令 l − 1 l-1 l1 的最远祖先为 x x x r r r 的最远祖先为 y y y,若 x = y x=y x=y,则说明我们已经能通过之前得出的结论计算出 ∑ [ l , r ] = s u m [ y ] − s u m [ x ] \sum[l,r]=sum[y]-sum[x] [l,r]=sum[y]sum[x],直接判断信息真假即可。
  反之,说明之前没有建立起有关 l l l r r r 的关系,我们让 y y y x x x 做父亲,然后尝试更新 s u m [ y ] sum[y] sum[y],画画图可以发现, s u m [ y ] = s u m [ l − 1 ] + d − s u m [ y ] sum[y]=sum[l-1]+d-sum[y] sum[y]=sum[l1]+dsum[y]
  具体内容参见代码。

代码

#include
using namespace std;
#define maxn 200010
int n,m,f[maxn],sum[maxn];
int fa(int x){
	if(f[x]==x) return x;
	int ret=fa(f[x]);
	sum[x]+=sum[f[x]];
	return f[x]=ret;
}
int main(){
	while(~scanf("%d%d",&n,&m)){
		for(int i=0;i<=n;i++){
			f[i]=i;
			sum[i]=0;
		} int ans=0;
		for(int i=1;i<=m;i++){
			int l,r,d;
			scanf("%d%d%d",&l,&r,&d);
			int fx=fa(l-1),fy=fa(r);
			if(fx!=fy){
				f[fy]=fx;
				sum[fy]=sum[l-1]+d-sum[r];
			}else if(sum[r]-sum[l-1]!=d) ans++;
		} printf("%d\n",ans);
	}
	return 0;
}

你可能感兴趣的:(带权并查集,带权并查集)