CF1830E Bully Sort
神仙题。
对于 \(1\) 到 \(n\) 的排列 \(P\),我们定义一次操作如下:
找到最大的 \(p_i\),使得 \(p_i\) 不等于 \(i\)
找到最小的 \(p_j\),使得 \(i < j\)
交换 \(p_i\) 和 \(p_j\)
定义 \(f(P)\) 为使排列 \(P\) 升序的最少操作次数。进行 \(q\) 次操作,每次操作交换 \(p_x\) 与 \(p_y\),你需要在每次操作后求出 \(f(P)\) 的值。
\(2 \leq n \leq 5 \times 10^5\),\(1 \leq q \leq 5 \times 10^4\)。
先考虑单个 \(f(P)\) 的计算,容易发现整个过程相当于对于从大到小的每个 \(p_i\),将其调整到下标为 \(p_i\) 的位置。
不妨令当前操作交换 \(p_i\) 和 \(p_j\),容易发现此时对于 \(x \in (p_i+1,n]\) 有 \(p_x=x\),同时又可知 \(p_i \not= i\),也就是说在当前序列中,若 \(p_x > p_i\),则 \(i < j < x\)。同时由于 \(p_j\) 是满足 \(i<j\) 的元素中最小的一个,所以对于所有 \(p_x < p_j\),均有 \(x < i < j\)。同时我们可以发现 \(p_i > p_j\),则整个序列形如下面的形式:
考虑交换 \(p_i\) 和 \(p_j\) 带来的影响。我们考虑如下两个量:
-
\(P\) 的逆序对总数
-
\(\sum\limits_{i=1}^{n} |p_i-i|\)
前者在交换 \(p_i\) 和 \(p_j\) 时,由于 \((p_i,p_j)\) 变为 \((p_j,p_i)\),则逆序对一定减少 \(1\)。我们考虑其他数的贡献。容易发现对于 \([1,p_j)\) 与 \((p_i,n]\) 中的值,其与 \(p_i\) 和 \(p_j\) 的相对位置不变且其下标不在 \((i,j)\) 中,所以不会产生贡献。接下来只考虑满足 \(k \in (i,j)\) 且 \(p_k \in (p_j,p_i)\) 的值即可。容易发现对于所有 \(k \in (i,j)\) 必然满足此条件,而对于这样的每个数都会减少 \(2\) 个逆序对。综合来看,交换 \(p_i\) 和 \(p_j\) 会将逆序对总数减少 \(1+2(j-i-1)=2j-2i-1\)。
接下来考虑 \(\sum\limits_{i=1}^{n} |p_i-i|\)。我们首先证明 \(i \geq p_j\) 与 \(j \leq p_i\),这一点是显然的。由于所有 \([1,p_j)\) 中的数下标都在 \([1,i)\) 中且所有 \((p_i,n]\) 中的数下标都在 \((j,n]\) 中,显然会有 \(i \geq p_j\) 与 \(j \leq p_i\)。其次,由 \(i < j\),我们可以推出 \(p_j \leq i < j\) 与 \(p_i \geq j > i\)。我们就有了下面的式子:
-
\(|p_i-i| + |p_j-j| = p_i-i+j-p_j\)
-
\(|p_j-i| + |p_i-j| = i-p_j+p_i-j\)
考虑将 \(p_i\) 与 \(p_j\) 交换,\(\sum\limits_{i=1}^{n} |p_i-i|\) 就会减少 \((p_i-i+j-p_j)-(i-p_j+p_i-j)=2j-2i\)。
此时我们可以得到关键结论:交换 \(p_i\) 和 \(p_j\) 会使 \(P\) 的逆序对总数减少 \(2j-2i-1\),同时使 \(\sum\limits_{i=1}^{n} |p_i-i|\) 减少 \(2j-2i\)。而我们知道,当排列 \(P\) 升序时,这两个值应当都为 \(0\)。不妨记初始时 \(P\) 的逆序对总数为 \(a\),\(\sum\limits_{i=1}^{n} |p_i-i|=b\),则进行的操作次数为 \(b-a\)。接下来的问题转化为动态维护 \(a\) 和 \(b\)。
\(b\) 的值可以在每次修改时直接更新,主要问题是维护 \(a\) 的值。
我们发现这就是排队。考虑分块做法,对于每一块维护一个 vector,对整块二分计算贡献,对散块暴力计算贡献。此时若块长为 \(B\),则单次修改复杂度为 \(O(\dfrac{n}{B} \log B + B)\)。平衡块长应该可以通过。
然后发现过不了。但是我们可以采用更高明的技巧,做到 \(O(n \sqrt{n})\) 的复杂度。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=500000,B=721;
int block_id[N+10],block_l[B+10],block_r[B+10],block_cnt;
bool cnt_pos[B+10][N+10];
int cnt_block[B+10][B+10];
int n,p[N+10],fenwick[N];
int my_abs(int num){
if(num>=0) return num;
else return -num;
}
long long pre_dis,pre_inv;
void modify(int pos,int val,bool cmd){
int dif;
if(cmd==false){
dif=-1;
}
else{
dif=1;
}
for(int i=block_id[pos];i<=block_cnt;i++){
cnt_pos[i][val]=cmd;
cnt_block[i][block_id[val]]+=dif;
}
}
int query(int pos,int val){
int ans=0;
if(block_id[pos]>1){
for(int i=1;i<=block_id[val]-1;i++){
ans+=cnt_block[block_id[pos]-1][i];
}
for(int i=block_l[block_id[val]];i<=val;i++){
ans+=cnt_pos[block_id[pos]-1][i];
}
}
for(int i=block_l[block_id[pos]];i<=pos;i++){
if(p[i]<=val){
ans++;
}
}
return ans;
}
void init(){
memset(block_l,0x3f,sizeof(block_l));
memset(block_r,-0x3f,sizeof(block_r));
for(int i=1;i<=N;i++){
block_cnt=block_id[i]=(i-1)/B+1;
block_l[block_id[i]]=min(block_l[block_id[i]],i);
block_r[block_id[i]]=max(block_r[block_id[i]],i);
}
}
int main(){
init();
int q;
scanf("%d %d",&n,&q);
for(int i=1;i<=n;i++){
scanf("%d",&p[i]);
pre_dis+=my_abs(p[i]-i);
pre_inv+=i-1;
if(i>1 && p[i]>1){
pre_inv-=query(i-1,p[i]);
}
modify(i,p[i],1);
}
while(q--){
int x,y;
scanf("%d %d",&x,&y);
int num_x=p[x],num_y=p[y];
if(num_x>num_y){
pre_inv--;
}
else{
pre_inv++;
}
int cnt,minn=min(num_x,num_y),maxn=max(num_x,num_y);
cnt=(query(y,maxn-1)-query(y,minn))-(query(x,maxn-1)-query(x,minn));
if(num_x>num_y){
pre_inv-=2*cnt;
}
else{
pre_inv+=2*cnt;
}
modify(x,num_x,0);
modify(y,num_y,0);
swap(p[x],p[y]);
modify(x,num_y,1);
modify(y,num_x,1);
pre_dis-=my_abs(num_x-x)+my_abs(num_y-y);
pre_dis+=my_abs(num_y-x)+my_abs(num_x-y);
printf("%lld\n",pre_dis-pre_inv);
}
return 0;
}
过了。
甚至比根号老哥好写说是。
甚至比树套树好写说是。
分块最高!

浙公网安备 33010602011771号