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

浙公网安备 33010602011771号