题目描述
给定一个 1 ∼ n1∼n 的排列 p_ipi,接下来有 mm 次操作,操作共两种:
- 交换操作:给定 xx,将当前排列中的第 xx 个数与第 x+1x+1 个数交换位置。
- 询问操作:给定 kk,请你求出当前排列经过 kk 轮冒泡排序后的逆序对个数。 对一个长度为 nn 的排列 p_ipi 进行一轮冒泡排序的伪代码如下:
for i = 1 to n-1:
if p[i] > p[i + 1]:
swap(p[i], p[i + 1])
输入格式
第一行两个整数 nn, mm,表示排列长度与操作个数。
第二行 nn 个整数表示排列 p_ipi。
接下来 mm 行每行两个整数 t_iti, c_ici,描述一次操作:
- 若 t_i=1ti=1,则本次操作是交换操作,x=c_ix=ci;
- 若 t_i=2ti=2,则本次操作是询问操作,k=c_ik=ci。
输出格式
对于每次询问操作输出一行一个整数表示答案。
输入输出样例
3 6 1 2 3 2 0 1 1 1 2 2 0 2 1 2 2
0 2 1 0
说明/提示
样例一解释
第一次操作:排列为 {1,2,3},经过 0 轮冒泡排序后为 {1,2,3},0 个逆序对。
第二次操作:排列变为 {2,1,3}。
第三次操作:排列变为 {2,3,1}。
第四次操作:经过0轮冒泡排序后排列变为 {2,3,1}{2,3,1},2 个逆序对。
第五次操作:经过1轮冒泡排序后排列变为 {2,1,3}{2,1,3},1 个逆序对。
第六次操作:经过2轮冒泡排序后排列变为 {1,2,3}{1,2,3},0 个逆序对。
数据范围与提示
对于测试点 1 ∼ 2:nn, m leqslant 100m⩽100。
对于测试点 3 ∼ 4:nn, m leqslant 2000m⩽2000。
对于测试点 5 ∼ 6:交换操作个数不超过 100100。
对于所有测试点:2 leqslant n2⩽n, m leqslant 2 imes 10^5m⩽2×105,t_i in {1,2}ti∈{1,2},1 leqslant x < n1⩽x<n,0 leqslant k < 2^{31}0⩽k<231。
解析:
本题是个结论题。。。
手玩几组冒泡排序就会发现,经历一次冒泡排序会发生这样的事情:
如果一个数左边有比它大的数:这个数消除不了逆序对
否则,这个数可以一直向后交换,直到遇到比它大的数
简单理解:如果它左边有比它厉害的,它就被干掉了,
否则它就一直向右碾压,直到遇到它打不过的人再停下。
具体操作:
我们记录第i位数前面比它大的数的数量为b[i],显然,当前序列的总逆序对数量就是所有的b之和通过对冒泡排序的观察,我们可以发现,每一遍冒泡排序都会使得所有b[i]=max(b[i]-1,0)
我们采用树状数组差分维护这一操作,令quera(t)为当ti=2,k=t时的答案
在数组最前面加入当前序列总逆序对数量,然后在第i位放b大于i的数字的数量的相反数,
因为这些数字在第i轮逆序对数均会减1
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+5;
int n,m,a[maxn],b[maxn],d[maxn];
ll c[maxn],ans;
inline int lowbit(int x)
{
return x&(-x);
}
inline void add(int x,ll val)
{
while(x<=n)
{
c[x]+=val;
x+=lowbit(x);
}
}
inline ll quera(int x)
{
ll res=0;
while(x>0)
{
res+=c[x];
x-=lowbit(x);
}
return res;
}
int main()
{
int opt,x,tmp=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
b[i]=i-1-quera(a[i]);
ans+=b[i],++d[b[i]];
add(a[i],1);
}
memset(c,0,sizeof(c));
add(1,ans);
for(int i=0;i<n;i++)
{
tmp+=d[i];
add(i+2,tmp-n);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&opt,&x);
x=min(x,n-1);
if(opt==1)
{
if(a[x]<a[x+1])
{
swap(a[x],a[x+1]);
swap(b[x],b[x+1]);
add(1,1);
add(b[x+1]+2,-1);
b[x+1]++;
}
else
{
swap(a[x],a[x+1]);
swap(b[x],b[x+1]);
add(1,-1);
b[x]--;
add(b[x]+2,1);
}
}
else printf("%lld
",quera(x+1));
}
return 0;
}