Increment and Swap
Increment and Swap
AtCoder 题目的构成,大抵都是分析出特殊性质后进行维护。
考虑到两种操作交替进行不利于分析。结合两种操作自身具有的性质,得出结论:
- 对于任意操作序列,均能找到与其等价的操作序列,其中所有 \(2\) 操作都在 \(1\) 操作前。
这里的等价是说,执行操作后的结果相同,且操作的次数相同。这意味着我们可以先用 \(+1\) 操作得到一个长度为 \(N\) 的序列 \(B\),满足 \(B_i \geq A_i\),接下来再用交换操作将 \(B\) 排序。
考虑这样操作会产生的代价。第一部分的代价显然为 \(\sum\limits_{i=1}^{n} b_i - \sum\limits_{i=1}^{n} a_i\),第二部分的代价根据经典结论,其等价于逆序对个数,也就是 \(\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{i-1} [b_j > b_i]\)。将两者写在一起便于观察,得到 \(\sum\limits_{i=1}^{n} (b_i - a_i + \sum\limits_{j=1}^{i-1} [b_j > b_i])\),分离出与 \(B\) 有关的量,最终得到:
接下来我们的目标就是最小化 \(\sum\limits_{i=1}^{n} (b_i + \sum\limits_{j=1}^{i-1} [b_j > b_i])\),注意到下列结论:
- 存在一种最优方案,使得所有的 \(b_i\) 都在序列 \(A\) 中出现。
这是很多题里常见的结论,容易从感性理解方面去进行证明。接下来就可以离散化 \(A\) 了。
考虑使用这个式子推导暴力算法,我们声称从 \(1\) 到 \(n\) 考虑所有的 \(i\),取满足 \(x + \sum\limits_{j=1}^{i-1} [b_j > x]\) 最小的 \(x\) 作为 \(b_i\),若有多个取最小的 \(x\)。这个策略是正确的,从感性理解方面,其既保证了当前的贡献最小,又尽量让后面的贡献变小。
维护数组 \(C\),初始时 \(c_i = i\)。\(C\) 数组表示了选择 \(b_i = x\) 时,产生的贡献是 \(c_x\):
-
找到 \(\geq a_i\) 的下标中最小的 \(c\),记为 \(c_j\)。
-
令 \(b_i = j\),则将 \([1,j-1]\) 中所有 \(c_j\) 的贡献 \(+1\)。
这个过程容易使用线段树维护,时间复杂度 \(O(n \log n)\)。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[200010],num_list[200010];
struct Data{
int pos,num;
}INF=(Data){0x3f3f3f3f,0x3f3f3f3f};
bool operator <(const Data &lhs,const Data &rhs){
if(lhs.num!=rhs.num){
return lhs.num<rhs.num;
}
else{
return lhs.pos<rhs.pos;
}
}
struct Node{
int l,r,tag;
Data minn;
}seg[800010];
void pushup(int id){
seg[id].minn=min(seg[id*2].minn,seg[id*2+1].minn);
}
void pushdown(int id){
seg[id*2].minn.num+=seg[id].tag;
seg[id*2].tag+=seg[id].tag;
seg[id*2+1].minn.num+=seg[id].tag;
seg[id*2+1].tag+=seg[id].tag;
seg[id].tag=0;
}
void build(int id,int l,int r){
seg[id].l=l;
seg[id].r=r;
seg[id].tag=0;
if(l==r){
seg[id].minn=(Data){l,num_list[l]};
}
else{
int mid=(l+r)>>1;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
pushup(id);
}
}
void modify(int id,int l,int r){
if(l<=seg[id].l && seg[id].r<=r){
seg[id].minn.num++;
seg[id].tag++;
return ;
}
pushdown(id);
if(l<=seg[id*2].r){
modify(id*2,l,r);
}
if(seg[id*2+1].l<=r){
modify(id*2+1,l,r);
}
pushup(id);
}
Data query(int id,int l,int r){
if(l<=seg[id].l && seg[id].r<=r){
return seg[id].minn;
}
pushdown(id);
Data minn=INF;
if(l<=seg[id*2].r){
minn=min(minn,query(id*2,l,r));
}
if(seg[id*2+1].l<=r){
minn=min(minn,query(id*2+1,l,r));
}
return minn;
}
int main(){
int n,num_cnt=0;
long long ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
ans-=a[i];
num_list[++num_cnt]=a[i];
}
sort(num_list+1,num_list+num_cnt+1);
num_cnt=unique(num_list+1,num_list+num_cnt+1)-(num_list+1);
for(int i=1;i<=n;i++){
a[i]=lower_bound(num_list+1,num_list+num_cnt+1,a[i])-num_list;
}
build(1,1,num_cnt);
for(int i=1;i<=n;i++){
Data b=query(1,a[i],num_cnt);
ans+=b.num;
if(b.pos>1){
modify(1,1,b.pos-1);
}
}
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号