[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++

相關文章
相關標籤/搜索