原题链接:https://www.luogu.com.cn/problem/P6186
冒泡排序
题目描述
给定一个 1 ∼ n 1 ∼ n 1∼n 的排列 p i p_i pi ,接下来有 m m m 次操作,操作共两种:
- 交换操作:给定 x x x,将当前排列中的第 x x x 个数与第 x + 1 x+1 x+1 个数交换位置。
- 询问操作:给定 k k k,请你求出当前排列经过 k k k 轮冒泡排序后的逆序对个数。 对一个长度为 n n n 的排列 p i p_i pi
进行一轮冒泡排序的伪代码如下:
for i = 1 to n-1:
if p[i] > p[i + 1]:
swap(p[i], p[i + 1])
输入格式
第一行两个整数 n n n, m m m,表示排列长度与操作个数。
第二行 n n n 个整数表示排列 p i p_i pi。
接下来 m m m 行每行两个整数 t i t_i ti, c i c_i ci ,描述一次操作:
若
t
i
=
1
t_i=1
ti=1,则本次操作是交换操作,
x
=
c
i
x=c_i
x=ci ;
若
t
i
=
2
t_i=2
ti=2,则本次操作是询问操作,
k
=
c
i
k=c_i
k=ci 。
输出格式
对于每次询问操作输出一行一个整数表示答案。
输入输出样例
输入 #1
3 6
1 2 3
2 0
1 1
1 2
2 0
2 1
2 2
输出 #1
0
2
1
0
说明/提示
样例一解释
第一次操作:排列为 { 1 , 2 , 3 } \{1,2,3\} {1,2,3},经过 0 0 0 轮冒泡排序后为 { 1 , 2 , 3 } \{1,2,3\} {1,2,3}, 0 0 0个逆序对。
第二次操作:排列变为 { 2 , 1 , 3 } \{2,1,3\} {2,1,3}。
第三次操作:排列变为 { 2 , 3 , 1 } \{2,3,1\} {2,3,1}。
第四次操作:经过 0 0 0 轮冒泡排序后排列变为 { 2 , 3 , 1 } \{2,3,1\} {2,3,1}, 2 2 2 个逆序对。
第五次操作:经过 1 1 1 轮冒泡排序后排列变为 { 2 , 1 , 3 } \{2,1,3\} {2,1,3}, 1 1 1 个逆序对。
第六次操作:经过 2 2 2 轮冒泡排序后排列变为 { 1 , 2 , 3 } \{1,2,3\} {1,2,3}, 0 0 0 个逆序对。
数据范围与提示
对于测试点 1 ∼ 2: n , m ≤ 100 n,m \leq 100 n,m≤100。
对于测试点 3 ∼ 4: n , m ≤ 2000 n,m \leq 2000 n,m≤2000。
对于测试点 5 ∼ 6:交换操作个数不超过 100 100 100。
对于所有测试点: 2 ≤ n , m ≤ 2 × 1 0 5 2 \leq n,m \leq 2 \times 10^5 2≤n,m≤2×105, t i ∈ { 1 , 2 } t_i \in \{1,2\} ti∈{1,2}, 1 ≤ x < n 1 \leq x < n 1≤x<n, 0 ≤ k < 2 31 0 \leq k < 2^{31} 0≤k<231 。
题解
思维题还是要多手玩几组样例,找到了规律后面再套数据结构都是小意思。
随便写个数列: 5 , 3 , 4 , 1 , 2 , 5,3,4,1,2, 5,3,4,1,2,
对于每个数,统计一下位置在它之前而数值又大于它的数的个数
f
r
o
n
t
[
i
]
front[i]
front[i]:
5
,
3
,
4
,
1
,
2
0
,
1
,
1
,
3
,
3
5,3,4,1,2\\ 0,1,1,3,3
5,3,4,1,20,1,1,3,3
做一次冒泡排序: 3 , 4 , 1 , 2 , 5 0 , 0 , 2 , 2 , 0 3,4,1,2,5\\ 0,0,2,2,0 3,4,1,2,50,0,2,2,0
如果没有观察出来可以再做一次: 3 , 1 , 2 , 4 , 5 0 , 1 , 1 , 0 , 0 3,1,2,4,5\\ 0,1,1,0,0 3,1,2,4,50,1,1,0,0
可以发现凡是大于 0 0 0的 f r o n t [ i ] front[i] front[i]都被减了一,而已经为 0 0 0的则不受影响。
于是我们将统计的 f r o n t [ i ] front[i] front[i]放到桶里做更直观的统计, c o t [ i ] cot[i] cot[i]表示满足 f r o n t [ j ] = = i front[j]==i front[j]==i的 f r o n t [ j ] front[j] front[j]的个数:
初始情况:
c
o
t
[
i
]
=
2
,
0
,
2
i
=
1
,
2
,
3
cot[i]=2,0,2\\ \ \ \ \ \ \ \ i=1,2,3
cot[i]=2,0,2 i=1,2,3
此时整个数列中逆序对数量
=
1
×
2
+
2
×
0
+
3
×
2
=
8
=1\times 2+2\times 0+3\times 2=8
=1×2+2×0+3×2=8
做一次冒泡:
c
o
t
[
i
]
=
0
,
2
,
0
i
=
1
,
2
,
3
cot[i]=0,2,0\\ \ \ \ \ \ \ \ i=1,2,3
cot[i]=0,2,0 i=1,2,3
此时整个数列中逆序对数量
=
1
×
0
+
2
×
2
+
3
×
0
=
4
=1\times 0+2\times 2+3\times 0=4
=1×0+2×2+3×0=4
做两次冒泡:
c
o
t
[
i
]
=
2
,
0
,
0
i
=
1
,
2
,
3
cot[i]=2,0,0\\ \ \ \ \ \ \ \ i=1,2,3
cot[i]=2,0,0 i=1,2,3
此时整个数列中逆序对数量
=
1
×
2
+
2
×
0
+
3
×
0
=
2
=1\times 2+2\times 0+3\times 0=2
=1×2+2×0+3×0=2
容易观察出每做一次冒泡排序, c o t [ i ] cot[i] cot[i]就会整体左移一位,而整个数列的逆序对数目 = ∑ i = 1 n i × c o t [ i ] =\sum_{i=1}^n i\times cot[i] =∑i=1ni×cot[i],所以我们用两个树状数组分别维护一下 c o t [ i ] cot[i] cot[i]和 i × c o t [ i ] i\times cot[i] i×cot[i]就可以快速给出每次询问的答案。
对于修改操作,根据被交换的两个数的大小关系对两个树状数组维护的量做出相应修改即可。
代码
#include<bits/stdc++.h>
#define lb(v) (v&(-v))
using namespace std;
const int M=2e5+5;
int n,m,mx,tmp,front[M],que[M],cot[M];
long long tot;
struct Tree{
long long node[M];
void add(int v,int delta){for(;v&&v<=n;v+=lb(v))node[v]+=delta;}
long long sum(int v)
{
long long r=0;
for(;v;v-=lb(v))r+=node[v];
return r;
}
}tree[2];
void in()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d",&que[i]);
++cot[front[i]=i-1-tree[1].sum(que[i]-1)];
tot+=front[i];
tree[1].add(que[i],1);
mx=max(mx,front[i]);
}
}
void ac()
{
memset(tree[1].node,0,sizeof(tree[1].node));
for(int i=1;i<=mx;++i)tree[1].add(i,1ll*i*cot[i]),tree[0].add(i,cot[i]);
for(int i=1,a,b;i<=m;++i)
{
scanf("%d%d",&a,&b);
if(a-1)
{
if(b>=mx){puts("0");continue;}
printf("%lld\n",tot-tree[1].sum(b)-1ll*(tree[0].sum(n)-tree[0].sum(b))*b);
}
else
{
swap(que[b],que[b+1]);
swap(front[b],front[b+1]);
//printf("%d %d\n",que[b],que[b+1]);
if(que[b]<que[b+1])
{
tree[1].add(front[b],-front[b]);
tree[0].add(front[b],-1);
--front[b];
tree[1].add(front[b],front[b]);
tree[0].add(front[b],1);
--tot;
}
else
{
tree[1].add(front[b+1],-front[b+1]);
tree[0].add(front[b+1],-1);
++front[b+1],mx=max(mx,front[b+1]);
tree[1].add(front[b+1],front[b+1]);
tree[0].add(front[b+1],1);
++tot;
}
}
}
}
int main()
{
in(),ac();
system("pause");
}