【题解】Strange Queries

题意:给定一个整数集合包含 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]); } } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530277.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示