题意
长度为 N(1≤N≤100000) N ( 1 ≤ N ≤ 100000 ) 的数组 A A ,和一个整数 ,有 N N 次操作每次删除一个位置
问不包含失效位置的最接近 的区间权值和
分析
并没有完全看懂出题者的题解,补题时非常感谢https://blog.youkuaiyun.com/qq_36797743/article/details/81604424的启发!
首先对于每次使一个点失效,我们可以将其离线,倒过来做。这样问题就变成每次使一个点生效。
考虑到每次使某点生效,本质为将该点与(已经生效的)左右区间进行合并。对于合并的对象,我们可以考虑到,保存每个区间的值是不可行的。那么我们可以保存某个区间内所有的前缀和(或后缀和),由于 [l,r] [ l , r ] 区间和为 Sr−Sl−1 S r − S l − 1 ,我们可以在两个区间进行合并时,通过“启发式方法”(通过枚举小区间内的前缀值,在另一区间内查找使得最接近 K K 的另一前缀和,然后将较小的区间放到较大区间中),同时计算出 区间和与 差的绝对值的最小值。
具体的操作是为每一个位置开一个 set s e t ,之后固定枚举区间的左端点或右端点:
当左区间 Size≤ S i z e ≤ 右区间 Size S i z e 时,对于每一个左边界 D=|K−(Sr−Sl−1)|=|(K+Sl−1)−Sr| D = | K − ( S r − S l − 1 ) | = | ( K + S l − 1 ) − S r |
只要在右区间中查询 Si≤K+Sl−1≤Sj S i ≤ K + S l − 1 ≤ S j ,之后更新最小值 D D 。
之后将左区间合并入右区间中。
当右区间 左区间 Size S i z e 时,对于每一个右边界 D=|K−(Sr−Sl−1)|=|(Sr−K)−Sl−1| D = | K − ( S r − S l − 1 ) | = | ( S r − K ) − S l − 1 |
只要在左区间中查询 Si≤Sr−K≤Sj S i ≤ S r − K ≤ S j ,之后更新最小值 D D 。
之后将右区间合并入左区间中。
最后要考虑的是当我们对每一个点 创建 set s e t 时,只插入了 Si S i ,而 [l,r] [ l , r ] 区间和的表示是 Sr−Sl−1 S r − S l − 1 ,那么就会漏掉 l=leftmost i l = leftmost i 的情况,我们可以在每次区间合并前,将 leftmost i leftmost i 当作左边界强制更新一下最小值。
显而易见的是,通过启发式方法进行合并的均摊时间复杂度为 O(nlogn) O ( n log n ) ,由于在 set s e t 中的查询需要 logn log n 的时间,故整体时间复杂度为 O(nlog2n) O ( n log 2 n ) 。
代码
/*C++11 WARNING*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5+5;
int n, k;
int a[N], s[N], f[N], lm[N], d[N], ans[N];
set<int> st[N];
int findfa(int x){return x==f[x]?x:f[x]=findfa(f[x]);}
void init(int n){
memset(f, 0, (n+2)*sizeof(int));
for(int i = 1; i <= n; i++) lm[i] = i-1;
}
void mergeSet(int x, int y, int& mn){
int fx = findfa(x), fy = findfa(y);
ll sp = (ll)k+s[lm[fx]];
auto iter = st[fy].lower_bound(sp);
if(iter != st[fy].end()) mn = min((ll)mn, abs(sp-*iter));
if(iter != st[fy].begin()){iter--; mn = min((ll)mn, abs(sp-*iter));}
if(st[fx].size() >= st[fy].size()){
f[fy] = fx;
for(int v: st[fy]){
sp = (ll)v-k;
iter = st[fx].lower_bound(sp);
if(iter != st[fx].end()) mn = min((ll)mn, abs(sp-*iter));
if(iter != st[fx].begin()){iter--; mn = min((ll)mn, abs(sp-*iter));}
}
for(int v: st[fy]) st[fx].insert(v);
st[fy].clear();
}else{
f[fx] = fy; lm[fy] = lm[fx];
for(int v: st[fx]){
sp = (ll)k+v;
iter = st[fy].lower_bound(sp);
if(iter != st[fy].end()) mn = min((ll)mn, abs(sp-*iter));
if(iter != st[fy].begin()){iter--; mn = min((ll)mn, abs(sp-*iter));}
}
for(int v: st[fx]) st[fy].insert(v);
st[fx].clear();
}
}
int main()
{
while(scanf("%d%d", &n, &k) != EOF){
init(n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
s[i] = s[i-1] + a[i];
}
for(int i = 1; i <= n; i++) scanf("%d", &d[i]);
int mn = 0x7fffffff;
for(int i = n; i >= 1; i--){
if(mn == 0) {ans[i] = 0; continue;}
int& x = d[i];
mn = min(mn, abs(a[x] - k));
st[x].clear();
st[x].insert(s[x]);
f[x] = x;
if(f[x-1]) mergeSet(x-1, x, mn);
if(f[x+1]) mergeSet(x, x+1, mn);
ans[i] = mn;
}
for(int i = 1; i <= n; i++) printf("%d\n", ans[i]);
}
return 0;
}