gym102500 J. Jackdaws And Crows

题意:

给定一个数组和两种操作,问把数组变成正负相间(不能0)的最小代价。

操作一:花费代价C,把数组中的一些数+1,再取数组中的一些数-1

操作二:花费代价R,删除一个数

思路:

对于一个数 x,进行至少 |x|+1 次操作一可以把 x 变成正数或者负数,把此时的 x 称为自由态(?)。若操作一的次数太少,x 的符号不能改变,称为非自由态(+/-)。

设经过若干次操作一后,有一段 -???+???+ ,则在前两个非自由态中必须得去掉一个。发现无论去掉第一个还是第二个都不会影响第三个。事实上,每对相邻非自由数都是独立的。

用一个链表维护所有非自由态位置。从小到大枚举操作一的次数。每次把某些数 x 从非自由态变成自由态,只需考虑 x的前驱、x、x的后继三个点对答案的影响。然后从链表中删除 x。

若进行0次操作一,那就要把所有0删除。其他数全是非自由的,算一下答案。

若进行1次操作一,所有的0都变成自由态

继续枚举操作一的次数,更新答案。

const signed N = 5e5 + 3;
ll n, a[N], C, R, l[N], r[N];
PLL b[N];

void del(int x) { //删除链表节点
    r[l[x]] = r[x], l[r[x]] = l[x];
}
bool f(int i, int j) { //需不需要删其中一个
    if(i < 1 || i > n || j < 1 || j > n) return 0;
    return (j-i)%2==0 && a[i]*a[j]<0 || (j-i)%2 && a[i]*a[j]>0;
}

signed main() {
    iofast;
    cin >> n >> C >> R;
    for(int i = 1; i <= n; i++) cin >> a[i], b[i] = {abs(a[i]), i};

    sort(b + 1, b + 1 + n);

    //初始化链表
    for(int i = 1; i <= n; i++) l[i] = i-1, r[i] = i+1;

    //进行0次操作1, 删除所有0
    ll ans = 0, p = 0; //上一个非零位置
    for(int i = 1; i <= n; i++)
        if(!a[i]) ans += R; //0直接删除
        else {
            if(p && a[p] * a[i] > 0) ans += R; //删一个
            p = i;
        }

    //进行1次操作一, 把0变成?
    ll res = 0; p = 0;
    for(int i = 1; i <= n; i++)
        if(!a[i]) del(i);
        else res += f(p,i) * R, p = i;

    ans = min(ans, res + C);

    for(int i = 1; i <= n; i++) if(b[i].fi) {
        auto [v, id] = b[i];
        res += (f(l[id],r[id]) - f(l[id],id) - f(id,r[id])) * R;
        del(id);
        ans = min(ans, (v+1)*C + res);
    }

    cout << ans;
}

posted @ 2022-03-30 14:38  Bellala  阅读(73)  评论(0)    收藏  举报