【题解】Strange Queries

这篇博客探讨了一种数据结构和算法问题,涉及权值线段树与区间动态规划的结合使用。具体场景是在整数集合中处理增加或删除元素,并计算在满足特定条件下的最小代价。博主详细介绍了离线处理的方法,包括状态转移方程、区间合并策略以及如何更新线段树节点。通过这个实例,读者可以了解到如何高效地解决这类问题。
摘要由CSDN通过智能技术生成

题意:给定一个整数集合包含 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| aiaj 。有 q q q 次修改,每次会在集合中增加或删除一个数。 n , q ≤ 1 0 5 n,q\leq 10^5 n,q105

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) 函数:

  1. 如果其中之一为空,则直接返回另一个节点 。
  2. 否则执行转移。如果 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]);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值