[CSP-S模拟测试]:虎(DFS+贪心)

题目传送门(内部题15)


输入格式

第一行一个整数$n$,表明点数
接下来$n-1$行,每行三个数$x,y,z$,表明点$i$与$x$之间有一条边,若$y$为$0$表明初始为白色,不然为黑色,若$z$为$0$表明不对最终颜色作要求,不然表明要求为黑色。
html


输出格式

达到目的的最少操做多少次数。c++


样例

样例输入:算法

7
1 0 1
1 1 1
2 0 1
2 0 1
3 1 1
3 0 1
spa

样例输出:3d

3htm


数据范围与提示

对于$30\%$的数据,全部的$x$等于$1$。
对于$70\%$的数据,全部边最终都必须为黑色
对于$100\%$的数据,$n\leqslant 1,000,000$。
blog


题解

先看数据范围,$n\leqslant 1,000,000$(注意是一百万,不是十万,可能只有我数不清几个$0$了吧?),这只能容许咱们$\Theta(n)$。get

$70\%$算法:it

仍是先从部分分下手,先来考虑$70\%$的数据,全部便最终都必须为黑色,考虑贪心。class

比方说有下面这样一条链:

咱们能够选择翻转$1\sim 3$和$4\sim 5$,也能够选择先翻转$1\sim 5$再将$3\sim 4$翻转回来,可是都须要两步,因此咱们能够贪心的扫每一条链,直到扫到一条黑边为止,把这中间的都翻转便可。

那么如今来考虑许多边连向一个点的状况:

比方说上面这张图,一共有三个白边连向点$1$,你可能首先会下意识的觉得须要翻转三次(聪明的你也可能没有),可是仔细一想,咱们能够把这其中任意两个翻转合并,如翻转$2\sim 1\sim 4$这条路径,而后再翻转$1\sim 7$这条路径以达到目的。

那么不妨这样讲,对于多条边连向一个点的状况,其所需的翻转次数即为$\left \lceil \frac{黑边个数}{2} \right \rceil$。

时间复杂度:$\Theta(n)$。

指望得分:$70$分。

实际得分:$60$分。

$100\%$算法:

显然对于一道$T1$来讲,咱们应该$A$掉它。

发现每条边只会被要求为黑色,或者是任意颜色,因此在来贪心。

对于任意颜色,咱们能够无视它,这不太好想,但仔细一想也是对的,我也不知道该怎么解释了,本身体会吧?因此咱们能够把这种边缩掉,个人方法是用一个相似并查集思想的东西,可是比并查集简单的多。

时间复杂度:$\Theta(n)$。

指望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to,w;}e[2000001];
int head[1000001],cnt=1;
int n;
bool vis[2000001];
int fa[1000001];
int ans;
void add(int x,int y,int w)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	e[cnt].w=w;
	head[x]=cnt;
}
void dfs(int x)
{
	for(int i=head[x];i;i=e[i].nxt)
		if(!vis[i]&&!e[i].w)
		{
			vis[i]=vis[i^1]=1;
			dfs(e[i].to);
			return;
		}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=2;i<=n;i++)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		if(!z)fa[i]=fa[x];
		else
		{
			add(i,fa[x],y);
			add(fa[x],i,y);
		}
	}
	for(int x=1;x<=n;x++)
	{
		int sum=0;
		for(int i=head[x];i;i=e[i].nxt)
			if(!vis[i]&&!e[i].w)
			{
				vis[i]=vis[i^1]=1;
				dfs(e[i].to);
				sum++;
			}
		if(sum&1)ans+=sum/2+1;
		else ans+=sum/2;
	}
	printf("%d",ans);
	return 0;
}

rp++

相关文章
相关标签/搜索