[2021.10.3NOIP模擬] 資料恢復
阿新 • • 發佈:2021-10-04
一般違法行為和犯罪之間沒有不可逾越的鴻溝!
的貢獻。
前言
掉大坑了。
題目
連結就不給了。
題目大意
給定一棵 \(n\) 個點的樹,每個點上有兩個權值 \(a_i,b_i\),每次可以選擇一個點,條件是它的所有祖先都已經被選擇過,這樣可以組成一個排列 \(p_i\),令其代價為:
\[\sum_{i=1}^n(b_{p_i}\times \sum_{j=i+1}^na_{p_j}) \]求最大代價。
\(1\le n\le 3\times 10^5;1\le a_i,b_i\le 5000;1\le f_i<i.\)
\(f_i\) 是 \(i\) 節點的父親,似乎保證了 \(a_1=b_1=0.\)
樣例
樣例輸入1
4 1 1 2 0 0 3 1 5 1 4 1
樣例輸出1
14
不會還指望我再搬幾個樣例吧?
講解
首先我們不難推出優先選 \(\frac{b_i}{a_i}\) 最大的最優,這個只需要隨便寫個式子就好。
然後我就掉進了一個大坑,我覺得既然都有這個性質了,那麼正解一定是某個妙妙貪心,說不定有反悔什麼的。
然後我就無了。
如果不走偏的話其實正解也不難想,我們還是看上面那個東西,拋開祖先優先的限制不看,先選 \(\frac{b_i}{a_i}\) 最大的最優,但是如果有這個限制呢?
首先祖先一定是先選的,這毋庸置疑,然後你想選的這個最大值如果可以選了,一定會立即選它!所以我們可以考慮把最大值的那個點和它的祖先合併起來。
反覆合併,直到只剩一個點,每次合併產生 \(b_{F_i}\times a_i\)
注意這裡的 \(F_i\) 不是 \(f_i\),因為它的父親可能已經和它的爺爺合併了,所以這個 \(F_i\) 是它的祖先中沒有被合併的最近的那個點,用並查集實現。
時間複雜度 \(O(n\log_2n)\),瓶頸在堆。
程式碼
//12252024832524 #include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define TT template<typename T> using namespace std; typedef long long LL; const int MAXN = 300005; int n,f[MAXN]; LL ans; LL Read() { LL x = 0,f = 1; char c = getchar(); while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();} return x * f; } TT void Put1(T x) { if(x > 9) Put1(x/10); putchar(x%10^48); } TT void Put(T x,char c = -1) { if(x < 0) putchar('-'),x = -x; Put1(x); if(c >= 0) putchar(c); } TT T Max(T x,T y){return x > y ? x : y;} TT T Min(T x,T y){return x < y ? x : y;} TT T Min(T x){return x < 0 ? -x : x;} LL a[MAXN],b[MAXN]; struct node { LL a,b;int ID; bool operator < (const node &px)const{ return b*px.a < px.b*a; } }; priority_queue<node> q; int F[MAXN]; int findSet(int x){if(F[x]^x)F[x] = findSet(F[x]);return F[x];} void unionSet(int u,int v) { u = findSet(u); v = findSet(v); if(u^v) F[u] = v; } int main() { // freopen("data.in","r",stdin); // freopen("data.out","w",stdout); n = Read(); for(int i = 1;i <= n;++ i) F[i] = i; for(int i = 2;i <= n;++ i) f[i] = Read(); for(int i = 1;i <= n;++ i) a[i] = Read(),b[i] = Read(); for(int i = 2;i <= n;++ i) q.push(node{a[i],b[i],i}); while(!q.empty()) { node t = q.top(); q.pop(); if(t.a^a[t.ID] || t.b^b[t.ID]) continue; int fa = findSet(f[t.ID]); if(!fa) continue; unionSet(t.ID,fa); ans += b[fa] * a[t.ID]; a[fa] += a[t.ID]; b[fa] += b[t.ID]; q.push(node{a[fa],b[fa],fa}); } Put(ans); return 0; }