Зсув значень#12284

Зсув значень (Розбір)

Автор проблеми:

Titan493ASST

Підготовлено:

Hasan Saleh (MathModel)

Редаговано:

Hasan Saleh (MathModel)

Розв'язок

Ми можемо лише переміщати значення вниз: кожна операція забирає деяке a\ge 1 з нелистового і додає його точно одному дитині, тому внутрішні вузли зменшуються, але ніколи не можуть опуститися нижче , тоді як листи ніколи не зменшуються. Отже, фінальне максимальне значення листа принаймні дорівнює початковому максимальному значенню листа; більше того, в оптимальному процесі нам ніколи не потрібно додавати до листа, який вже досяг цього максимуму, тому ми можемо зафіксувати фінальне максимальне значення як

mx=\max_{\ell\in L} val[\ell]\ \text{(початкове)}.

Тоді мінімізація \max_L-\min_L стає такою ж, як максимізація досяжного мінімального значення листа ; відповідь буде mx-X.

Оскільки внутрішні вузли повинні зберігати принаймні , кожен нелист може "пожертвувати" максимум val[u]-1 одиниць загалом своїм нащадкам. Саме тому ми замінюємо val[u]\leftarrow val[u]-1 для нелистів у коді; після цього кожне val[u] є залишковим переносимим надлишком у (листи зберігають своє повне значення). Тепер зафіксуйте цільове мінімальне і запитайте, чи можемо ми зробити так, щоб кожен лист був принаймні . Для листа його чистий надлишок/дефіцит відносно дорівнює val[\ell]-X; якщо він негативний, піддерево, що містить , потребує X-val[\ell] одиниць з верхніх вузлів, якщо позитивний, то цей надлишок не може бути переміщений вгору, тому він марний для предків. Це призводить до обходу DFS знизу вгору з масивом , ініціалізованим як sum[u]=val[u] (після для нелистів). Коли ми повертаємося з дитини , ми вже обчислили її чистий надлишок для досягнення всіх листів у піддереві . Якщо є листом, ми спочатку робимо sum[v]\mathrel{-}=X, перетворюючи його на val[v]-X. Тоді батько може бути змушений платити лише дефіцити, тому ми додаємо \min(0,sum[v]) до . Позитивний ігнорується, оскільки він не може подорожувати вгору. Після обробки всіх дітей, представляє, скільки надлишку залишається в піддереві після задоволення всіх вимог листів там (негативне означає залишковий попит). Таким чином, цільове є здійсненним, якщо після DFS на корені ми маємо sum[1]\ge 0. Здійсненність є монотонною в , тому ми бінарно шукаємо найменший діапазон mid=mx-X в : встановлюємо X=mx-mid, запускаємо перевірку DFS, і якщо sum[1]<0, мінімум занадто високий (збільшуємо ), в іншому випадку записуємо і намагаємося менше. Отриманий є мінімально можливим \max_L-\min_L. Кожна перевірка є і ми робимо \mathcal{O}(\log mx) перевірок, тому загалом \mathcal{O}(n\log mx).

#include <bits/stdc++.h>
#define int long long
#define ld long double
#define nl "\n"
#define ull unsigned long long
#define rv return void
#define str string
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define vec vector
#define fixed(n) fixed << setprecision(n)
#define Moageza ios::sync_with_stdio(false);cout.tie(NULL);cin.tie(NULL);
using namespace std;
vec<int>sum,v,vis;
vec<vec<int>>adj;
void dfs(int node,int par,int val){
    for(auto it:adj[node]){
        if(it==par)continue;
        dfs(it,node,val);
        if(vis[it]==1){
            sum[it]-=val;
        }
        sum[node]+=min(0LL,sum[it]);
    }
}
void solve()
{
   int n;cin>>n;
  v.assign(n+1,0),vis.assign(n+1,0),adj.assign(n+1,{});;
   for(int i=1;i<=n;i++)cin>>v[i];
   for(int i=0;i<n-1;i++){
    int a,b;cin>>a>>b;
    adj[a].push_back(b);
    adj[b].push_back(a);
    vis[a]++,vis[b]++;
   }
   int mx=0;
   vec<int>ans;
   for(int i=1;i<=n;i++){
    if(vis[i]==1){
        mx=max(mx,v[i]);
        ans.push_back(i);
    }
    else v[i]--;
   }
   int l=0,r=mx,res=0;
   while(l<=r){
    int mid=(l+r)/2;
   sum.assign(n+1,0);
   for(int i=1;i<=n;i++)sum[i]=v[i];
   dfs(1,0,mx-mid);
    // cout<<sum[1]<<" ";
    if(sum[1]<0)l=mid+1;
    else {
        res=mid;
        r=mid-1;
        // cout<<mid<<" ";
    }
   }
   cout<<res;
}
signed main()
{
    Moageza
     #ifndef ONLINE_JUDGE
        freopen("input.txt", "r", stdin); freopen("output.txt", "w", stdout);
    #endif
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
        cout << nl;
    }
    return 0;
}