2025-10-12?

ICPC EC 区域赛成都站 F

image

先用 lagrange 乘数法解决 给定分组情况(告诉你每个组里面的元素个数 和 s 总和)后的答案求解问题。如果你最终分成了 t 组,每组 \(s_i\) 的综合和物品的数量为 \(\texttt{sum_1,cnt_1..sum_t,cnt_t}\),lagrange 乘数法给出答案就是 \(\sum\limits_{i=1}^t \sqrt{\texttt{sum_i}\times\texttt{cnt_i}}\)

因为题面没有写恰好 M 类,但是我们可以把恰好扩写到题面里面,恰好 M 类对应 wqs 二分。那么我们写一个 wqs 二分,dp 使用二分栈来实现决策单调性的转移即可。做完了。

首先考虑:如果某个组的 \(s_{i,\min}< s_{j,\max}\) 那么我们将这两个物品交换,答案肯定更优。因为根号运算有凹凸性保证。那么我们可以把所有物品按照 \(s\) 排序,设 \(f_i,g_i\)\(i\) 数划分若干组,拼上 wqs 二分赋予的 reward/penalty 后最大的收益是多少,\(g_i\) 表示该划分方案对应的段数。

容易写出转移方程,容易验证该转移方程满足四边形不等式。很难评价的一道复习题。这题好像是之前在读别人游记的时候读到 wqs 二分了,没读到 wqs 二分感觉大概率是想不出来的。不过知道是 wqs 二分之后后面就都我自己玩的,挺有意思。

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
const int N = 200000 + 10;

int n, m;
int s[N];
double sumv[N];
double dp[N];
int tim[N];

inline double cost(int i, int j, double pnl){
    // i < j
    return dp[i] + sqrt(double(j - i)) * sqrt(sumv[j] - sumv[i]) + pnl;
}

struct Node {
    int idx; // decision point i
    int l;   // valid interval left
    int r;   // valid interval right
};

inline bool check(double pnl){
    // initialize
    for(int i=0;i<=n;i++){ dp[i]=1e100; tim[i]=n+5; }
    dp[0] = 0.0;
    tim[0] = 0;

    deque<Node> dq;
    // initial decision 0 is optimal for j in [1..n]
    dq.push_back({0, 1, n});

    for(int j=1;j<=n;j++){
        // pop front decisions whose interval ended before j
        while(!dq.empty() && dq.front().r < j) dq.pop_front();
        if(dq.empty()){
            // shouldn't normally happen if logic correct, but safe-guard:
            dp[j] = 1e100;
            tim[j] = n+5;
        } else {
            int i = dq.front().idx;
            dp[j] = cost(i, j, pnl);
            tim[j] = tim[i] + 1;
        }

        // we don't need to insert decision for j if j==n (no future j)
        if(j == n) continue;

        // create new candidate from decision j, valid initially on [j+1, n]
        Node cur = {j, j+1, n};

        // maintain deque's back: find first position where new decision becomes better
        while(!dq.empty()){
            Node last = dq.back();
            int i_prev = last.idx;
            int L = last.l, R = last.r;
            // find smallest pos in [L,R] such that cost(j,pos) <= cost(i_prev,pos)
            int pos = R + 1;
            int lo = L, hi = R;
            while(lo <= hi){
                int mid = (lo + hi) >> 1;
                double c_new = cost(j, mid, pnl);
                double c_old = cost(i_prev, mid, pnl);
                if(c_new <= c_old){
                    pos = mid;
                    hi = mid - 1;
                } else lo = mid + 1;
            }

            if(pos <= last.l){
                // new decision is no worse than last on entire last interval -> pop last
                dq.pop_back();
            } else {
                // new decision only wins from pos..R, so shrink last.r and set cur.l = pos
                dq.back().r = pos - 1;
                cur.l = pos;
                break;
            }
        }
        // if deque became empty, cur.l remains j+1 (or set correctly)
        if(cur.l <= cur.r) dq.push_back(cur);
        // else if cur.l > cur.r then new decision never wins in future range -> do not push
    }

    return tim[n] <= m;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m;
    double l = 0, r = 0, ans = 0;
    for(int i=1;i<=n;i++){
        cin >> s[i];
        r += sqrt((double)s[i]);
    }
    sort(s+1, s+n+1);
    sumv[0] = 0.0;
    for(int i=1;i<=n;i++) sumv[i] = sumv[i-1] + (double)s[i];
    r -= sqrt(sumv[n]);

    // binary search on penalty
    while(r - l > 1e-10){
        double mid = (l + r) / 2.0;
        if(check(mid)){
            ans = mid;
            r = mid;
        } else l = mid;
    }
    // compute final dp with best penalty ans
    check(ans);
    cout << fixed << setprecision(10) << dp[n] - ans * m << "\n";
    return 0;
}

由于一些原因我确实不想写二分栈了,这份代码的二分栈部分都是 GPT5 生成的……vp 的时候最后五分钟写了一个对的分治一直在 TLE,很烦。

posted @ 2025-10-12 13:28  没学完四大礼包不改名  阅读(39)  评论(2)    收藏  举报