报表统计
题目链接:https://www.luogu.com.cn/problem/P1110
题解:
考虑离散化,利用两颗线段树来维护合适的信息求解两个询问。
MIN_GAP:求相邻的差值绝对值最小。建立线段树存储差值的出现次数,询问时求差值最小的那个即可。记位置
i
i
i初始值为
a
[
i
]
a[i]
a[i],新插入的值为
b
[
i
]
b[i]
b[i](初始
b
[
i
]
=
a
[
i
]
b[i]=a[i]
b[i]=a[i]),当在位置
i
i
i插入一个新的数
k
k
k时,原本的差值
a
b
s
(
a
[
i
+
1
]
−
b
[
i
]
)
abs(a[i+1]-b[i])
abs(a[i+1]−b[i])出现次数减一,新出现两个差值
a
b
s
(
b
[
i
+
1
]
−
k
)
,
a
b
s
(
k
−
b
[
i
]
)
abs(b[i+1]-k), abs(k-b[i])
abs(b[i+1]−k),abs(k−b[i])出现次数加1(注意一下
i
=
=
n
i == n
i==n时,只需更新第一个)。然后另
b
[
i
]
=
k
b[i] = k
b[i]=k即可。
MIN_SORT_GAP:求最接近的两个元素的差值。线段树维护数出现的次数即可。设
a
n
s
2
ans2
ans2,初始为原数组中排序后相邻元素差值的最小值。更新时,统计
≤
k
\le k
≤k的数的数量sum,求第
s
u
m
,
s
u
m
+
1
sum,sum+1
sum,sum+1个元素的值,与
k
k
k求差值,更新
a
n
s
2
ans2
ans2即可。
a
n
s
2
ans2
ans2即结果。最后将k的信息放入线段树。若
a
n
s
2
=
=
0
ans2 == 0
ans2==0后,差值不会更小,则停止查询更新的操作,以降低时间复杂度。
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-6
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 2500100;
const int mod = 10000;
int a[maxn], b[maxn], c[maxn], p1[maxn*3], p2[maxn*3];
int op[maxn], x[maxn], y[maxn];
int lisan(int n, int a[]);
void Update(int l, int r, int x, int y, int k, int p[]);
int querysum(int l, int r, int x, int k, int p[]);
int query(int l, int r, int x, int k, int p[]);
int main()
{
int n, m, i, j, k, cnt, pos, ans1=INF, ans2=INF;
char str[20];
scanf("%d %d", &n, &m);
for(i=1;i<=n;i++){
scanf("%d", &a[i]);
b[i] = c[i] = a[i];
}
sort(c+1, c+1+n);
for(i=2;i<=n;i++)ans2 = min(ans2, c[i]-c[i-1]);
cnt = n;
for(i=2;i<=n;i++)c[++cnt] = abs(a[i]-a[i-1]);
for(i=0;i<m;i++)
{
scanf(" %s", str);
if(str[0] == 'I'){
op[i] = 1;
scanf("%d %d", &x[i], &y[i]);
c[++cnt] = y[i];
c[++cnt] = abs(y[i]-b[x[i]]);
if(x[i] != n)c[++cnt] = abs(a[x[i]+1]-y[i]);
b[x[i]] = y[i];
}else if(str[4] == 'G')op[i] = 2;
else op[i] = 3;
}
for(i=1;i<=n;i++)b[i] = a[i];
cnt = lisan(cnt, c);
for(i=1;i<=n;i++){
int pos = lower_bound(c+1, c+1+cnt, a[i])-c;
Update(1, cnt, pos, 1, 1, p1);
if(i==1)continue;
pos = lower_bound(c+1, c+1+cnt, abs(a[i]-a[i-1]))-c;
Update(1, cnt, pos, 1, 1, p2);
}
for(i=0;i<m;i++)
if(op[i] == 1){
if(ans2 != 0){
pos = lower_bound(c+1, c+1+cnt, y[i])-c;
int sum = querysum(1, cnt, pos, 1, p1);
int id = query(1, cnt, sum, 1, p1);
ans2 = min(ans2, y[i]-c[id]);
if(p1[1] != sum){
id = query(1, cnt, sum+1, 1, p1);
ans2 = min(ans2, abs(c[id]-y[i]));
}
Update(1, cnt, pos, 1, 1, p1);
}
int ch = abs(y[i]-b[x[i]]);
pos = lower_bound(c+1, c+1+cnt, ch)-c;
Update(1, cnt, pos, 1, 1, p2);
if(x[i] != n){
ch = abs(a[x[i]+1]-b[x[i]]);
pos = lower_bound(c+1, c+1+cnt, ch)-c;
Update(1, cnt, pos, -1, 1, p2);
ch = abs(a[x[i]+1]-y[i]);
pos = lower_bound(c+1, c+1+cnt, ch)-c;
Update(1, cnt, pos, 1, 1, p2);
b[x[i]] = y[i];
}
}else if(op[i] == 2)printf("%d\n", c[query(1, cnt, 1, 1, p2)]);
else printf("%d\n", ans2);
return 0;
}
int lisan(int n, int a[])
{
int top = 1, i;
sort(a+1, a+1+n);
for(i=2;i<=n;i++)
if(a[top] != a[i])a[++top] = a[i];
return top;
}
void Update(int l, int r, int x, int y, int k, int p[])
{
p[k] += y;
if(l == r)return;
int mid = (l+r)/2;
if(x <= mid)Update(l, mid, x, y, 2*k, p);
else Update(mid+1, r, x, y, 2*k+1, p);
}
int querysum(int l, int r, int x, int k, int p[])
{
if(l == r)return p[k];
int mid = (l+r)/2;
if(x<=mid)return querysum(l, mid, x, 2*k, p);
else return p[2*k]+querysum(mid+1, r, x, 2*k+1, p);
}
int query(int l, int r, int x, int k, int p[])
{
if(l == r)return l;
int mid = (l+r)/2;
if(x<=p[2*k])return query(l, mid, x, 2*k, p);
else return query(mid+1, r, x-p[2*k], 2*k+1, p);
}