P11189 「KDOI-10」水杯降溫

blind5883發表於2024-10-17

P11189 「KDOI-10」水杯降溫 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)

慶賀吧,第一個真正意義上的自己幹出來的紫題。總用時 4h。

時間複雜度 \(O(n\log n)\),對於每個點我們去找它可以吹氣的最大次數和最小次數。如果一個點的最小次數大於它的最大次數,或者在計算父節點 u 最大次數時 j 減去的次數大於 j 最大次數,那麼不可能成功吹冷。其餘情況都可以吹冷。

對於最大次數採用二分驗證答案方法,設當前減去的總數為 k,那麼 \(m = a[u]\) 就會變成 m - k,一般 k 是大於 m 的,為了讓 m 變為 0,需要總體加熱 k - m。那麼對於子節點來說,這些子節點最多減少 k 次,同時都必須加上 k - m,那麼對每一個 j 判斷,如果 \(b = a[j]\),b + k - m 如果大於 0,那麼 j 就必須減去 s = b + k - m 個,我們就讓 k 減去 s,如果 k - s 小於 0,說明我們需要的減不夠,那麼這種狀態就不行,注意,如果 s 超過了 j 的最大次數,也是不行的。不斷進行。特別的如果 u 是葉節點,那麼它的最大次數無窮大。求出了最大合適的 k 之後需要和子節點最大次數之比對,取比較小的那個最為 u 的最大次數。因為最大不能超過子節點的最大限度。

對於最小次數,則是子節點最小次數之和與當前 \(a[u]\) 中,大的那個。因為需要滿足雙方最小。

對於最大次數,可以為負數,為了判斷如,這種情況:

0
1
2
1
-2 -1
s

綜上寫出程式碼即可。較短。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

typedef long long LL;

const int N = 500010;
const LL INF = 1e12 + 10;

int h[N], ne[N], e[N], idx;
int n, m, key = 601;
LL a[N], cnt[N], low[N];

bool check(LL u, LL k)
{
    LL res = k;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        LL s = k - a[u] + a[j];
        if (s > cnt[j]) return false; // 需要減去的不能超過它能承受的最大的
        if (s > 0) res -= s;
        if (res < 0) return false;
    }
    return true;
}

LL find1(LL u)
{
    LL l = -INF, r = INF;
    
    while (l < r)
    {
        LL mid = l + r + 1 >> 1;
        if (check(u, mid)) l = mid;
        else r = mid - 1;
    }
    
    return l;
}

bool dfs(int u)
{
	LL sum = 0, sum2 = 0;
	bool flag = 0;
	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (dfs(j)) return true;
		sum += low[j];
		sum2 += cnt[j];
		flag = true;
 	}
	if (flag) 
	{
	    cnt[u] = min(find1(u), sum2);
	   // cout << 'a';
	   //cout << find1(u) << endl;
	}
	else cnt[u] = INF;
	
	if (flag) low[u] = max(sum, a[u]);
	else low[u] = -INF;
// 	printf("%d %lld %lld\n", u, low[u], cnt[u]);
	if (low[u] > cnt[u]) return true;
	return false;
}

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

int main()
{
	int T;
	cin >> T >> T;
	for (int u = 1; u <= T; u ++ )
	{

		cin >> n;
		idx = 0;
		for (int i = 0; i <= n; i ++ ) 
		{
			h[i] = -1;
		}
		for (int i = 2; i <= n; i ++ ) 
		{
			int x;
			cin >> x;
			add(x, i);
		}
		for (int i = 1; i <= n; i ++ ) 
		{
			scanf("%lld", &a[i]);
		} 
		int flag = dfs(1);
		if (flag) puts("Shuiniao");
		else puts("Huoyu");
	}
	
	return 0;
}


/*
hack

0
1
2
1
-2 -1
s

0
1
7
1 2 2 3 3 5 
21 18 16 -80 14 2 12
s

0
1
7
1 2 2 3 2 3 
1206191 1201814 1172698 -778167 116182 -2139946 1052140 
s

0
1
7
1 2 2 3 3 5 
1347945 1347877 1347443 -536656 1344473 2906 1240697 
s

*/

相關文章