一種似乎更快抽象的解法?
題面。
正文
看這道題,給定序列 \(l,r,c\),要求重構 \(l,r,c\) 使得 \(\sum_{i=1}^n(r_i-l_i) \times c_i\) 最小。
首先可以想到的就是儘量讓小的 \(r_i-l_i\) 乘上大的 \(c_i\)。這樣子看來 \(c_i\) 幾乎不需要更多的處理,僅需從小到大(或從大到小)排個序。
來看如何求出優秀的 \(r_i-l_i\)。
對於每一個 \(r_i-l_i\),要使它們的差最小,最好的方法自然是使這兩個數儘可能地相近並且 \(l_i<r_i\) 仍成立。因為題目中保證了初始序列的 \(l\) 與 \(r\) 滿足 \(l_i<r_i\)。所以不存在構造不出滿足目標的 \(l,r\) 序列。
假設已經找出了 \(n\) 個最優的 \(r_i-l_i\),分別是 \(rl_1,rl_2 \sim rl_n\)。將它們展開後再合併會發現,無論 \(rl_i\) 的具體值是什麼,始終會滿足:
說明在保證 \(rl\) 的選擇最優的情況下,不管怎麼排列並不會影響最終結果。這應該是顯而易見的。
貪心的考慮一下,對於每個 \(r_i\),找到比它小的最大的 \(l_j\) 匹配,得到的 \(rl\) 即是最小。若有多個 \(r\) 匹配同一個 \(l\)。則讓最小的 \(r\) 匹配(能小則小)。
下面內容過於抽象所以請移步其它題解。
由於最近再練平衡樹,練得腦子有點抽,誒?\(l_i,r_i\) 互不相同。比 \(r_i\) 小的最大值?於是自然而然地用到了三棵平衡樹,來維護上面的 \(r,l\) 以及 \(rl\)。並且自己又非常懶惰,所以使用了 pb_ds。
標頭檔案:
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>//平衡樹
using namespace __gnu_pbds;
定義:
tree<ll , null_type , std::less<ll> , rb_tree_tag , tree_order_statistics_node_update> L , R;
tree<pair<ll , ll> , null_type , std::less<pair<ll , ll>> , rb_tree_tag , tree_order_statistics_node_update> RL;
在程式碼中用瞭如下操作:
-
insert
插入一個數。程式碼內使用L.insert(l)
。 -
erase
擦除一個數或迭代器。程式碼內使用L.erase(nl)
。 -
lower_bound
返回指向首個不小於給定鍵的元素的迭代器。程式碼內使用ll nl = *(--L.lower_bound(nr));
(注:自減操作是為了找到比 \(nr\) 小的最大值)。 -
order_of_key(x)
返回 \(x\) 以比較的排名。程式碼內使用ll order = RL.order_of_key({rl , 0});
。 -
find_by_order(x)
返回的排名 \(x\) 所對應元素的迭代器。程式碼內使用ll rl = RL.find_by_order(0) -> first;
。
當然,功能多多,碼量遠小於手寫平衡樹只是比賽時沒什麼用。
最後程式碼複雜度 \(O(Tn \log n)\),勉強能看。
PS:魔怔人平衡樹學傻了。
程式碼
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#define ll long long
#define fr(i , a , b) for(ll i = a ; i <= b ; ++i)
#define fo(i , a , b) for(ll i = a ; i >= b ; --i)
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>//平衡樹
using namespace __gnu_pbds;
using namespace std;
tree<ll , null_type , std::less<ll> , rb_tree_tag , tree_order_statistics_node_update> L , R;
tree<pair<ll , ll> , null_type , std::less<pair<ll , ll>> , rb_tree_tag , tree_order_statistics_node_update> RL;
ll T , n , l , r , c[200005];
inline bool cmp(ll x , ll y)
{
return x > y;
}
ll sum;
signed main()
{
// freopen("in.in" , "r" , stdin);
// freopen("out.out" , "w" , stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> T;
while(T--)
{
ll idx = 0;
cin >> n;
fr(i , 1 , n)
{
cin >> l;
L.insert(l);
}
fr(i , 1 , n)
{
cin >> r;
R.insert(r);
}
fr(i , 1 , n)
{
cin >> c[i];
}
fr(i , 1 , n)
{
ll nr = *(R.find_by_order(0));
ll nl = *(--L.lower_bound(nr));
RL.insert({nr - nl , ++idx});
L.erase(nl);
R.erase(nr);
}
sort(c + 1 , c + n + 1 , cmp);
sum = 0;
fr(i , 1 , n)
{
ll rl = RL.find_by_order(0) -> first;
sum += rl * c[i];
ll order = RL.order_of_key({rl , 0});
RL.erase(RL.find_by_order(order));
}
cout << sum << '\n';
}
return 0;
}