Sürüşən Dəyərlər#12284

Sürüşən Dəyərlər (Həll təhlili)

Problem Müəllifi:

Titan493ASST

Hazırlayan:

Hasan Saleh (MathModel)

Redaktə edən:

Hasan Saleh (MathModel)

Həll

Biz yalnız dəyəri aşağıya doğru hərəkət etdirə bilərik: hər əməliyyat bir yarpağa aid olmayan -dan a\ge 1 götürür və onu dəqiq bir uşağa əlavə edir, beləliklə daxili düyünlər azalır, lakin -dən aşağı düşə bilməz, yarpaqlar isə heç vaxt azalmır. Beləliklə, son maksimum yarpaq dəyəri ən azı ilkin maksimum yarpaq dəyəri olacaq; daha da optimallaşdırılmış prosesdə artıq bu maksimuma çatmış bir yarpağa əlavə etməyə ehtiyac yoxdur, buna görə də son maksimumu

mx=\max_{\ell\in L} val[\ell]\ \text{(ilkin)}.

Sonra \max_L-\min_L-ni minimuma endirmək, əldə edilə bilən minimum yarpaq dəyəri -ni maksimuma çatdırmaqla eynidir; cavab mx-X olacaq.

Çünki daxili düyünlər ən azı saxlamaq məcburiyyətindədir, hər bir yarpağa aid olmayan cəmi val[u]-1 vahidini öz nəsillərinə "bağışlaya" bilər. Bu, kodda yarpaqlara val[u]\leftarrow val[u]-1 əvəz etməyimizin dəqiq səbəbidir; bundan sonra, hər val[u] -da qalan köçürülə bilən artıqdır (yarpaqlar tam dəyərlərini saxlayır). İndi hədəf minimum -ni düzəldin və hər yarpağı ən azı edə biləcəyimizi soruşun. Bir yarpaq üçün onun -ə nisbətən xalis artıq/defisit val[\ell]-X-dir; əgər mənfi olarsa, -i ehtiva edən alt ağacın yuxarıdan X-val[\ell] vahidinə ehtiyacı var, əgər müsbət olarsa, o əlavə yuxarıya köçürülə bilməz, buna görə də ata üçün faydasızdır. Bu, massivi ilə aşağıdan yuxarıya DFS-yə gətirib çıxarır, -nu val[u] (yarpaqlara -dən sonra) ilə başlamaqla. Bir uşağı -dən qayıdarkən, artıq -nin alt ağacında bütün yarpaqların -ə çatması üçün xalis artıq -ni hesablamışıq. Əgər bir yarpaqdırsa, əvvəlcə sum[v]\mathrel{-}=X edirik, onu val[v]-X-ə çeviririk. Sonra ata yalnız defisitləri ödəməyə məcburdur, buna görə də -ya \min(0,sum[v]) əlavə edirik. Müsbət yuxarıya köçürülə bilmədiyi üçün nəzərə alınmır. Bütün uşaqları emal etdikdən sonra, -nun alt ağacında bütün yarpaq tələblərini ödədikdən sonra qalan artıq miqdarı (mənfi qalan tələb) göstərir. Beləliklə, hədəf mümkündür, əgər DFS-dən sonra kökdə sum[1]\ge 0 varsa. Mümkünlük -də monotonikdir, buna görə də intervalında ən kiçik mid=mx-X-ni ikili axtarış edirik: X=mx-mid təyin edin, DFS yoxlamasını həyata keçirin, əgər sum[1]<0-da minimum çox yüksəkdirsə (artırın ), əks halda -ni qeyd edin və daha kiçikini sınayın. Nəticədə minimum mümkün \max_L-\min_L-dir. Hər yoxlama -dir və biz \mathcal{O}(\log mx) yoxlamaları edirik, buna görə də ümumi \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;
}