[省選聯考2022] 序列變換
阿新 • • 發佈:2022-04-20
好題。
考慮先對括號包含關係對括號序列進行構樹。
那麼考慮實際上一/二操作的實質是:
在同一層中:每次可以選擇兩個點 \((A,B)\),以 \(val_A * x + val_B * y\) 代價把 \(B\) 丟到下一層,要求每一層只有一個數。
考慮分類討論:
設本層有 \(m\) 個,最小值是 \(mn\),最大值是 \(mx\) .
\(x = 1,y = 1\)
一個顯然的最優方案是:
每個數都會有一次代價,然後每下放一個數都會有一個另外的數的代價,那麼答案有:
\((m - 2) * mn + sum\)
即每次留在這層的都是 \(mx\) 。
\(x = 0,y = 1\)
顯然也每次留在這層的是 \(mx\)
\(x = 1,y = 0\)
聽說原本只有這麼一種情況的。
考慮一種貪心:每次都往下以最小值的代價傳數,最後看保留的是哪一位數:
考慮每個數最終都會到一個位置保留,除了最後一個位置不用付代價外都要付代價
那麼自然想到把最大值給留到最後。
但是我們發現這樣在只有兩個的時候不成立,因為如果傳最大值,而不傳最小值有可能影響後續取數答案。
那麼考慮找到第一個加上上面傳下的數量大於\(2\)的位置然後分段,那麼從前面來的要麼是字首最大值,要麼是字首最小值,考慮兩種情況都跑即可。
小細節:如果有全域性最大值,取位置最後的,來保證最小值影響範圍大。
點選檢視程式碼
//晦暗的宇宙,我們找不到光,看不見盡頭,但我們永遠都不會被黑色打倒。——Quinn葵因 #include<bits/stdc++.h> #define ll long long #define N 400005 char s[N << 1]; int n,x,y; inline int read(){int x;scanf("%d",&x);return x;} int now = 0; using std::vector; vector<ll>G[N]; using std::multiset; multiset<ll>S; ll siz,sum,mn,mx; #define IT std::set<ll>::iterator ll ans; inline void sub1(){ for(int i = 1;i <= n;++i){ for(auto v : G[i]) S.insert(v),siz ++ ,sum += v; IT it = S.begin();mn = *it;it = S.end();--it;mx = *it; ans = ans + sum + (siz - 2) * mn; S.erase(it);siz -= 1;sum -= mx; } } ll MX; inline void sub2(){ for(int i = 1;i <= n;++i){ for(auto v : G[i]) S.insert(v),siz ++ ,sum += v; IT it = S.begin();mn = *it;it = S.end();--it;mx = *it; ans = ans + sum - mx; S.erase(it);siz -= 1;sum -= mx; } } int d; inline ll work(int now){ // std::cout<<"WORK "<<now<<"\n"; ll res = 0; for(int i = now;i <= n;++i){ for(auto v : G[i]) S.insert(v),siz ++ ,sum += v; IT it = S.begin();mn = *it;it = S.end();--it;mx = *it; if(i >= d){--it,mx = *it;res = res + (siz - 2) * mn + mx;S.erase(it);siz -= 1;sum -= mx;} else{res = res + (siz - 2) * mn + mx;S.erase(it);siz -= 1;sum -= mn;} // std::cout<<"DEP "<<mx<<" "<<mn<<" "<<res<<"\n"; } return res; } int flg; ll SI; ll MN; inline void sub3(){ //nothing haha for(int i = 1;i <= n;++i) for(auto v : G[i]){if(v > MX)MX = v,d = i;} ans = work(1); S.clear();sum = siz = 0; flg = 0; for(int i = 1;i <= n;++i){ if(G[i].size() != 1 && flg){ MX = 0; MN = *S.begin(); for(int j = i;j <= n;++j) for(auto v : G[j]){if(v > MX)MX = v,d = i;} ans = std::min(ans,SI - MN + work(i));return ; } if(G[i].size() == 2)flg = 1; for(auto v : G[i]) S.insert(v),siz ++ ,sum += v,SI += flg * v; IT it = S.end();--it;mx = *it; S.erase(it);siz -= 1;sum -= mx; } } int main(){ // freopen("bracket.in","r",stdin); // freopen("bracket.out","w",stdout); scanf("%d%d%d",&n,&x,&y); scanf("%s",s + 1); for(int i = 1;i <= n * 2;++i){ if(s[i] == '(')now ++ ; if(s[i] == ')')now -- ; if(s[i] == '(')G[now].push_back(read()); } if(x == 1 && y == 1)sub1(); if(x == 0 && y == 1)sub2(); if(x == 1 && y == 0)sub3(); std::cout<<ans<<"\n"; }