HDU 3038 How Many Answers Are Wrong

传送门

带权并查集。
有两个人,A先写一个数组(1~N),然后B在不看的前提下挑一个子序列,让A回答子序列的和,这么来M次,其中A会回答一些假话,但这些假话都是可以通过前面的话推断出来真假的那种假话,没法判别真假的话都是真话。问你这些话里面总共多少假话。

首先,对这道题而言,能够判断真假的前提一定是已知子序列AB(或BC),判断子序列C(或A)的和的真假(A=B+C。(实际这两种情况是一起实现的,因为可以互相转化)
HDU 3047类似,首尾互相链起来的子序列在一个圈子里面,然而这里有一个问题,给定的查询都是全闭区间,这样相邻区间的首尾并不重合,我们统统换成左开右闭区间

然后,我们拿这个权值表示的是两点间的子序列的和,但是,细想一下会发现,我们不仅要知道上下结点之间子序列的和,还要知道上下结点的大小关系(否则传递会乱套的)。和上道题不一样(而且上道题本身就是环形关系),这个权值关系本身是不带方向的!

所以,就要想办法加上这个方向信息,有两种方法。
第一种比较厉害,就是靠这个权值的正负来表示方向,绝对值表示和。其中,若当前结点大于父节点,权值为负;若小于,为正。这样想的时候肯定要分情况,但是写出来代码却是一句搞定(对没错,6种情况就是可以用一句归纳,惊不惊喜?),这样写出来看起来很玄妙,实则不可能一眼看出来,必须分情况归纳总结。(感觉有向量计算的意味在里面)
第二种很傻瓜,因为我们可以利用结点本身的值来判断大小关系,那么我们只允许值较小的结点成为值较大的结点的父节点。但这样我们放弃了性能(平衡规则),但是省了编写时间。
当然,还有第三种,还按照第一种那么分情况,但是因为不利用权值正负了,所以分情况没法合并。

第一种

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;

const int MAXN = 200000 + 5;
int N, M;
int pre[MAXN];
int dis[MAXN];
int counter;

int f(int x)
{
	if (pre[x] < 0) return x;
	int t = f(pre[x]);
	dis[x] = dis[x] + dis[pre[x]];
	return pre[x] = t;
}

void u(int a, int b, int s)
{
	int f1 = f(a);
	int f2 = f(b);
	if (f1 == f2)
	{
		if (dis[b] + s != dis[a]) counter++;
		return;
	}
	if (pre[f1] <= pre[f2])
	{
		pre[f1] += pre[f2];
		pre[f2] = f1;
		dis[f2] = dis[a] - s - dis[b];
	}
	else
	{
		pre[f2] += pre[f1];
		pre[f1] = f2;
		dis[f1] = s + dis[b] - dis[a];
	}
}

int main()
{
	int a, b, s;
	for (; ~scanf("%d%d", &N, &M);)          // 没说多组数据
	{
		memset(pre, -1, sizeof pre);
		memset(dis, 0, sizeof dis);
		counter = 0;

		for (; M--;)
		{
			scanf("%d%d%d", &a, &b, &s);
			u(a - 1, b, s);
		}
		printf("%d\n", counter);
	}

    return 0;
}

第二种

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;

const int MAXN = 200000 + 5;
int N, M;
int pre[MAXN];
int dis[MAXN];
int counter;

int f(int x)
{
	if (pre[x] < 0) return x;
	int t = f(pre[x]);
	dis[x] = dis[x] + dis[pre[x]];
	return pre[x] = t;
}

void u(int a, int b, int s)
{
	int f1 = f(a);
	int f2 = f(b);
	if (f1 == f2)
	{
		//if (dis[b] + s != dis[a]) counter++;
		if (dis[b] - s != dis[a]) counter++;
		return;
	}
	/*if (pre[f1] <= pre[f2])
	{
		pre[f1] += pre[f2];
		pre[f2] = f1;
		dis[f2] = dis[a] - s - dis[b];
	}
	else
	{
		pre[f2] += pre[f1];
		pre[f1] = f2;
		dis[f1] = s + dis[b] - dis[a];
	}*/
	if (f1 < f2)
	{
		//pre[f1] += pre[f2];
		pre[f2] = f1;
		dis[f2] = dis[a] + s - dis[b];     //
	}
	else
	{
		//pre[f2] += pre[f1];
		pre[f1] = f2;
		dis[f1] = dis[b] - s - dis[a];     //
	}
}

int main()
{
	int a, b, s;
	for (; ~scanf("%d%d", &N, &M);)          // 没说多组数据
	{
		memset(pre, -1, sizeof pre);
		memset(dis, 0, sizeof dis);
		counter = 0;

		for (; M--;)
		{
			scanf("%d%d%d", &a, &b, &s);
			u(a - 1, b, s);
		}
		printf("%d\n", counter);
	}

	return 0;
}//19.3.20 傻瓜版本
posted @ 2019-03-21 00:15  CrossingOver  阅读(163)  评论(0编辑  收藏  举报