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
*/