P3237 [HNOI2014]米特運輸

P3237 [HNOI2014]米特運輸

第104道題讓我洛谷紅名。

(^ w ^)

題目描述

米特是D星球上一種非常神祕的物質,蘊含着巨大的能量。在以米特爲主要能源的D星上,這種米特能源的運輸和儲存一直是一個大問題。

D星上有N個城市,我們將其順序編號爲1到N,1號城市爲首都。這N個城市由N-1條單向高速通道連接起來,構成一棵以1號城市(首部)爲根的樹,高速通道的方向由樹中的兒子指向父親。樹按深度分層:根結點深度爲0,屬於第1層;根結點的子節點深度爲1,屬於第2層;依此類推,深度爲i的結點屬於第i+l層。

建好高速通道之後,D星人開始考慮如何具體地儲存和傳輸米特資源。由於發展程度不同,每個城市儲存米特的能力不盡相同,其中第i個城市建有一個容量爲A[i]的米特儲存器。這個米特儲存器除了具有儲存的功能,還具有自動收集米特的能力。

如果到了晚上六點,有某個儲存器處於未滿的狀態,它就會自動收集大氣中蘊含的米特能源,在早上六點之前就能收集滿;但是,只有在儲存器完全空的狀態下啓動自動收集程序纔是安全的,未滿而又非空時啓動可能有安全隱患。

早上六點到七點間,根節點城市(1號城市)會將其儲存器裏的米特消耗殆盡。根節點不會自動蒐集米特,它只接受子節點傳輸來的米特。

早上七點,城市之間啓動米特傳輸過程,傳輸過程逐層遞進:先是第2層節點城市向第1層(根節點城市,即1號城市)傳輸,直到第1層的儲存器滿或第2層的儲存器全爲空;然後是第3層向第2層傳輸,直到對於第2層的每個節點,其儲存器滿或其予節點(位於第3層)的儲存器全爲空;依此類推,直到最後一層傳輸完成。傳輸過程一定會在晚上六點前完成。

由於技術原因,運輸方案需要滿足以下條件:

  • 不能讓某個儲存器到了晚上六點傳輸結束時還處於非空但又未滿的狀態,這個時候儲存器仍然會啓動自動收集米特的程序,而給已經儲存有米特的儲存器啓動收集程序可能導致危險,也就是說要讓儲存器到了晚上六點時要麼空要麼滿;

  • 關於首都——即1號城市的特殊情況, 每天早上六點到七點間1號城市中的米特儲存器裏的米特會自動被消耗殆盡,即運輸方案不需要考慮首都的米特怎麼運走;

  • 除了1號城市,每個節點必須在其子節點城市向它運輸米特之前將這座城市的米特儲存器中原本存有的米特全部運出去給父節點,不允許儲存器中殘存的米特與外來的米特發生混合;

  • 運向某一個城市的若干個來源的米特數量必須完全相同,不然,這些來源不同的米特按不同比例混合之後可能發生危險。

現在D星人已經建立好高速通道,每個城市也有了一定儲存容量的米特儲存器。爲了滿足上面的限制條件,可能需要重建一些城市中的米特儲存器。你可以,也只能,將某一座城市(包括首都)中原來存在的米特儲存器摧毀,再新建一座任意容量的新的米特儲存器,其容量可以是小數(在輸入數據中,儲存器原始容量是正整數,但重建後可以是小數),不能是負數或零,使得需要被重建的米特儲存器的數目儘量少。
輸入輸出格式
輸入格式:

第一行是一個正整數N,表示城市的數目。
接下來N行,每行一個正整數,其中的第i行表示第i個城市原來存在的米特儲存器的容量。
再接下來是N-I行,每行兩個正整數a,b表示城市b到城市a有一條高速通道(a≠b)。

輸出格式:

輸出文件僅包含一行,一個整數,表示最少的被重建(即修改儲存器容量)的米特儲存器的數目。

輸入輸出樣例
輸入樣例#1:

5
5
4
3	
2
1
1 2
1 3
2 4
2 5

輸出樣例#1:

3

題解

感覺是道難題,我想了好久。

diss這種卡題面的出題人

50分算法

這應該是唯一的暴力吧
先設一下變量名:

名稱 意義
 fai\ fa_{i}  i\ i的父節點
 vi\ v_{i}  i\ i的容量
 fi\ f_{i}  i\ i節點不變情況下的改變節點數量的最小值
 Si\ S_{i}  i\ i節點子節點的集合

首先我們理解題意:

  • 一棵樹,滿足以下條件

    •  vi=xSivx\ v_{i}=\sum_{x\in S_{i}}v_{x}
    •  vx=viSi        ,xSi\ v_{x}=\frac{v_{i}}{|S_{i}|}\,\,\,\,\,\,\,\,,x\in S_{i}

求將一棵樹修改節點使之滿足以上性質的最小修改數。
我們稍加思索就能得出當一個節點的權值確定的時候,整棵樹就是完全確定的。那麼就有一個暴力的想法。
枚舉每一個節點不變的情況然後找最小值。這是容易操作的。代碼是容易實現的。

AC算法

現在再定義幾個變量。

名稱 意義
 gi\ g_{i}  vi\ v_{i}不變的情況下,根節點的權值
 li\ l_{i}  gi\ g_{i} vi\ v_{i}的倍數
 Ci\ C_{i}  i\ i節點的父親節點與根節點路徑上的點的集合

由於根節點權值確定的話整棵樹都確定,所以這樣就把確認一個節點值的情況轉化爲確認根節點的值的情況。
我們也可以得到以下結論,
 Ti\ T_{i}表示 i\ i點不變的情況下樹的點集與邊集
可有以下結論

gi=gjTi=Tj g_{i}=g_{j} \Leftrightarrow T_{i}=T_{j}

那麼我們可以求出所有的 gi\ g_{i},然後比較 gi\ g_{i}的值,若相等,則爲同一種情況。
而且有

gi=livi g_{i}=l_{i} \cdot v_{i}

li=ΠxCiSi l_{i}=\Pi_{x \in C_{i}}|S_{i}|

由此可見, li\ l_{i}可以在遞歸中求出。
隨後統計 gi\ g_{i}所有情況的個數。答案爲相同情況的個數的最大值與 n\ n的差。

你以爲你AC了嗎?
還沒完!!!
由於 li\ l_{i}可能爆long long,可能連50分都不到。
那麼我們需要一個強大的優化。
 li,gi\ l_{i},g_{i}都是乘積,所以可以用對數優化一下。

gi=li+log vi g_{i}=l_{i}+log\,v_{i}

li=xCilog Si l_{i}=\sum_{x \in C_{i}}log\,|S_{i}|

到這裏就可以AC了。

AC代碼

#include<bits/stdc++.h>
using namespace std;
double v[500100],g[500100];
vector<int> ee[500100];
long long n,cnt=1,ans=1;
void dfs(int x,double lo)
{
	g[x]=lo+log(v[x]);
	int i=0,siz=ee[x].size();
    while(i<siz)
	{
        int y=ee[x][i];
        dfs(y,lo+log((double)siz));
        ++i;
    }
}
int main()
{
	scanf("%lld",&n);
	int i=1;
	while(i<=n)
	{
		int aa;
		scanf("%d",&aa);
		v[i]=aa;
		++i;
	}
	i=1;
	while(i<n)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		ee[x].push_back(y);
		++i;
	}
	dfs(1,log(1.0));
	sort(g+1,g+n+1);
	i=1;
	while(i<=n)
	{
		if(g[i]-g[i-1]<=1e-8)
		{
            ++cnt;
            ans=max(ans,cnt);
        }
		else cnt=1;
		++i;
	}
	printf("%lld\n",n-ans);
	return 0;
}

/*
1996年:東方靈異傳(TOH1)
1997年:東方封魔錄(TOH2)
1997年:東方夢時空(TOH3)
1998年:東方幻想鄉(TOH4)
1998年:東方怪綺談(TOH5)
2002年:東方紅魔鄉(TOH6)
2003年:東方妖妖夢(TOH7)
2004年:東方萃夢想(TOH7.5)
2004年:東方永夜抄(TOH8)
2005年:東方花映冢(TOH9)
2005年:東方文花帖(TOH9.5)
2007年:東方風神錄(TOH10)
2008年:東方緋想天(TOH10.5)
2008年:東方地靈殿(TOH11)
2009年:東方星蓮船(TOH12)
2009年:東方非想天則(TOH12.3)
2010年:東方文花帖DS(TOH12.5)
2010年:東方三月精(TOH12.8)
2011年:東方神靈廟(TOH13)
2013年:東方心綺樓(TOH13.5)
2013年:東方輝針城(TOH14)
2014年:彈幕天邪鬼(TOH14.3)
2014年:東方深祕錄(TOH14.5)
2015年:東方紺珠傳(TOH15)
2017年:東方憑依華(TOH15.5)
2017年:東方天空璋(TOH16)
*/
//寫這些東西++RP

總結

題面可以卡人半小時。。。
其實50分代碼是很好像的,但作對不是很容易。當然也不是很難。運氣好就秒A,像我這樣不好的話就。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章