[BZOJ 4409] [Usaco2016 Feb]Circular barn

這裏寫圖片描述

覺得應該正經的寫一寫做法了 TAT 不能總是玄學啊顯然啊意會啊什麼的。

爲了方便理解…我們不妨認爲題意是給牛選k個集合點然後牛逆時針走到這k個點上。

首先是破環成鏈,嘛,相信大家都會。

然後這一步處理的手段和BZOJ 1096 有點兒像[雖然還沒有寫那道題], 我們令a[i]表示點i的牛數,sum[i] 表示牛數的前綴和,然後令 b[i] 表示 1 ~ n 的牛全部移動到1的代價。這樣做以後有什麼好處呢?我們發現 這時候 i ~ j 的牛全部移動到 i 點的代價就是 b[i] - b[j - 1] - (sum[i] - sum[j - 1]) * j ;
然後就好辦了,我們枚舉環的起始點爲 p ,然後枚舉正在給牛選第 L 個集合點, 推一推狀態轉移方程發現是可以斜率優化的,所以我們按順序每計算一個f[ L ][ i ]; 把 f[L - 1][i] 插入到凸包裏。 最後用 f[k - 1][p + n - 1] 更新答案即可;

時間複雜度 O(n ^ 2 * k * log n )

[沙茶不會用markdown…
[A了之後發現斜率也是單調的可以優化成n ^ 2 * k 的但是不管了
[作者編寫程序時腦子一片混亂,邊界情況可能和上文不太一樣….

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
inline void read(int &x){x=0;char c;while((c=getchar())<'0'||c>'9');for(x=c-'0';(c=getchar())>='0'&&c<='9';x=x*10+c-'0');}
const LL inf = 1e16;
const int N = 2010 ;

struct Point 
{
    LL x, y;

    Point() {}
    Point(LL x, LL y) : x(x), y(y) {}

    inline Point operator - (const Point & b) const
    {
        return Point(x - b.x, y - b.y);
    }
    inline LL operator ^ (const Point & b) const 
    {
        return x * b.y - y * b.x;
    }
};

struct Stack
{
    Point s[N];
    int top;

    void insert(Point u)
    {
        if (u.y <= s[top].y) return;
        while (top > 1 && ((s[top] - s[top - 1]) ^ (u - s[top])) <= 0) top--;
        s[++top] = u;
    }

#define calc(p) ((s[(p)].y - s[(p)].x * x))
    LL query(LL x)
    {
        int l = 1, r = top;
        while (l < r)
        {
            int mid = (l + r) >> 1;
            if (calc(mid) > calc(mid + 1)) l = mid + 1;
            else r = mid;
        }
        return calc(l);
    }
#undef calc
}T;

LL sum[N], b[N], f[2][N];
int a[N], n, k;

int main()
{
    read(n), read(k);
    for (int i = 1; i <= n; ++i)
        read(a[i]), a[i + n] = a[i];
    for (int i = 1; i <= n + n; ++i)
    {
        sum[i] = sum[i - 1] + a[i];
        b[i] = b[i - 1] + a[i] * (i - 1);
    }
    LL ret = inf;
    for (int i = 0, ed = n; i < n; ++i, ++ed)
    {
        for (int j = i + 1; j <= ed; ++j)
            f[0][j] = b[j] - b[i] - (sum[j] - sum[i]) * i;
        int t = 1;
        for (int l = 1; l < k; ++l)
        {
            T.s[T.top = 0] = Point(i + l, f[t ^ 1][i + l] - b[i + l] + sum[i + l] * (i + l));
            for (int j = i + l + 1; j <= ed; ++j)
            {
                f[t][j] = T.query(sum[j]) + b[j];
                T.insert(Point(j, f[t ^ 1][j] - b[j] + sum[j] * j));
            }
            t ^= 1;
        }
        ret = min(ret, f[t ^ 1][ed]);
    }
    printf("%lld\n", ret);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章