Description
给定一个 (1 ∼ n) 的排列 (p_i),接下来有 (m) 次操作,操作共两种:
- 交换操作:给定 (x),将当前排列中的第 (x) 个数与第 (x+1) 个数交换位置。
- 询问操作:给定 (k),请你求出当前排列经过 (k) 轮冒泡排序后的逆序对个数。
Solution
设 (c[i]) 表示数 (i) 前有几个比它大的
在一次冒泡排序中,所有非零的 (c[i]) 都减少 (1)
于是经过 (k) 次冒泡排序后的逆序数就是
[sum_{c[i]>k} (c[i]-k)=sum_{c[i]>k} c[i]- ksum_{c[i]>k} 1
]
开两个树状数组,(s[k],t[k]) 分别维护对 (c[i]=k) 时的 (sum c[i]) 和 (sum 1)
修改时,若 (a[x]<a[x+1]) 则交换后 (c[a[x]]+1)(这里的 (a[]) 是交换前的),反之 (c[a[x+1]]-1)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
struct bitree
{
int ar[N];
int lowbit(int t)
{
return t & (-t);
}
void add(int i, int v)
{
++i;
for (; i < N; ar[i] += v, i += lowbit(i));
}
int sum(int i)
{
int s = 0;
for (; i > 0; s += ar[i], i -= lowbit(i));
return s;
}
int query(int l,int r)
{
return sum(r+1)-sum(l);
}
} s,t,b;
int n,m,a[N],c[N],t1,t2;
signed main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1; i<=n; i++) cin>>a[i];
for(int i=1; i<=n; i++)
{
c[a[i]]=b.query(a[i]+1,n);
b.add(a[i],1);
}
for(int i=1; i<=n; i++)
{
s.add(c[i],c[i]);
t.add(c[i],1);
}
while(m--)
{
cin>>t1>>t2;
int x=t2;
if(t1==1)
{
s.add(c[a[x]],-c[a[x]]);
t.add(c[a[x]],-1);
s.add(c[a[x+1]],-c[a[x+1]]);
t.add(c[a[x+1]],-1);
if(a[x]<a[x+1])
{
c[a[x]]++;
}
else
{
c[a[x+1]]--;
}
swap(a[x],a[x+1]);
s.add(c[a[x]],c[a[x]]);
t.add(c[a[x]],+1);
s.add(c[a[x+1]],c[a[x+1]]);
t.add(c[a[x+1]],+1);
}
else
{
t2=min(t2,n);
cout<<s.query(t2+1,n)-t2*t.query(t2+1,n)<<endl;
}
}
}