CF1909C Heavy Intervals 題解

心海秋的墨木仄發表於2024-04-10

一種似乎更抽象的解法?

題面

正文

看這道題,給定序列 \(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\) 的具體值是什麼,始終會滿足:

\[\sum _{i=1}^n rl_i=\sum_{j=1}^n r_j - \sum _{k=1}^n l_k \]

說明在保證 \(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;

在程式碼中用瞭如下操作:

  1. insert 插入一個數。程式碼內使用 L.insert(l)

  2. erase 擦除一個數或迭代器。程式碼內使用 L.erase(nl)

  3. lower_bound 返回指向首個不小於給定鍵的元素的迭代器。程式碼內使用 ll nl = *(--L.lower_bound(nr));(注:自減操作是為了找到比 \(nr\) 小的最大值)。

  4. order_of_key(x) 返回 \(x\) 以比較的排名。程式碼內使用 ll order = RL.order_of_key({rl , 0});

  5. 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;
}

相關文章