题意:给定一个整数集合包含 A 1 , A 2 , . . . , A n A1,A2,...,A_n A1,A2,...,An ,我们定义整数集合的代价为:如果数的个数 ≤ 2 \leq2 ≤2 ,则代价为 0 0 0 ;否则将每个整数至少与一个另外的整数建立连接。两个整数建立连接的代价是 ∣ a i − a j ∣ |a_i-a_j| ∣ai−aj∣ 。有 q q q 次修改,每次会在集合中增加或删除一个数。 n , q ≤ 1 0 5 n,q\leq 10^5 n,q≤105 。
solution:
考点:权值线段树 + 区间 dp。
考虑离线处理,设 d p [ i ] [ j ] [ 0 / 1 ] ] [ 0 / 1 ] dp[i][j][0/1]][0/1] dp[i][j][0/1]][0/1] 表示区间 [ i , j ] [i,j] [i,j] 的左右端点 是/否 建立连接,除端点外都至少被建立一次连接的最小代价。同时记录 l p [ i ] [ j ] lp[i][j] lp[i][j] 表示区间 [ i , j ] [i,j] [i,j] 中最靠左的存在于集合中的点, r p [ i ] [ j ] rp[i][j] rp[i][j] 表示最靠右的点。
区间合并转移:
dp[p][i][j]=min(dp[p<<1][i][k]+dp[p<<1|1][l][j]+[k==0||l==0]lp[p<<1|1]-rp[p<<1]
然后是 m e g ( x , y ) meg(x,y) meg(x,y) 函数:
- 如果其中之一为空,则直接返回另一个节点 。
- 否则执行转移。如果 l p [ p ] = r p [ p ] lp[p]=rp[p] lp[p]=rp[p] 那么 d p [ 0 ] [ 1 ] = d p [ 1 ] [ 0 ] = 0 dp[0][1]=dp[1][0]=0 dp[0][1]=dp[1][0]=0 但是 d p [ 1 ] [ 1 ] = I N F dp[1][1]=INF dp[1][1]=INF 。
注意线段树的划分方式是唯一确定的。时间复杂度 O ( 2 4 ( n + q ) l o g ( n + q ) ) O(2^4(n+q)log(n+q)) O(24(n+q)log(n+q)) 。
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int mx = 2e5 + 5;
int n, m, lsh[mx], a[mx], cnt;
struct query {
int op, x;
} q[mx];
struct node {
int dp[2][2], lp, rp, siz;
} t[mx << 2], null;
int getx(int x) { return lower_bound(lsh + 1, lsh + 1 + cnt, x) - lsh; }
node meg(node x, node y) {
node now;
if (x.siz == 0)
return y;
if (y.siz == 0)
return x;
now.lp = x.lp;
now.rp = y.rp;
now.siz = x.siz + y.siz;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
now.dp[i][j] = INF;
for (int k = 0; k < 2; k++) {
for (int l = 0; l < 2; l++) {
int tmp = x.dp[i][k] + y.dp[l][j];
if (k == 0 || l == 0)
tmp += lsh[y.lp] - lsh[x.rp];
now.dp[i][j] = min(now.dp[i][j], tmp);
}
}
}
}
return now;
}
void update(int p, int l, int r, int x, int val) {
if (l == r) {
if (val == 0) {
t[p].lp = t[p].rp = l;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
if (i == 1 && j == 1)
t[p].dp[i][j] = INF;
else
t[p].dp[i][j] = 0;
}
}
t[p].siz = 1;
} else {
t[p].lp = t[p].rp = 0;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
t[p].dp[i][j] = 0;
}
}
t[p].siz = 0;
}
return;
}
int mid = l + r >> 1;
if (x <= mid)
update(p << 1, l, mid, x, val);
else
update(p << 1 | 1, mid + 1, r, x, val);
t[p] = meg(t[p << 1], t[p << 1 | 1]);
}
int main() {
int T;
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
cnt = 0;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), lsh[++cnt] = a[i];
for (int i = 1; i <= m; i++) {
scanf("%d%d", &q[i].op, &q[i].x);
q[i].op--;
if (q[i].op == 0)
lsh[++cnt] = q[i].x;
}
sort(lsh + 1, lsh + 1 + cnt);
fill(t, t + cnt * 4 + 1, null);
for (int i = 1; i <= n; i++) {
int x = getx(a[i]);
update(1, 1, cnt, x, 0);
}
for (int i = 1; i <= m; i++) {
int x = getx(q[i].x);
update(1, 1, cnt, x, q[i].op);
if (t[1].siz <= 1)
printf("0\n");
else
printf("%d\n", t[1].dp[1][1]);
}
}
}