题目传送门(内部题120)
输入格式
第一行,两个正整数$n,m$。
第二行,$n$个正整数$a_1,a_2,...,a_n$,保证$1\leqslant a_i\leqslant n$,可能存在相同值。
第三行,$m$个正整数$j_1,j_2,...,j_m$,保证$1\leqslant j_k\leqslant n$。
输出格式
第一行,第一个整数表示操作前的逆序对数量,接下来$m$个整数表示每次操作后的逆序对数量。
样例
样例输入:
6 2
1 3 4 2 6 1
2 3
样例输出:
6 3 1
数据范围与提示
样例解释:
第一次操作:由$1\ 3\ 4\ 2\ 6\ 1$变为$1\ 1\ 4\ 2\ 6\ 3$。
第二次操作:由$1\ 1\ 4\ 2\ 6\ 3$变为$1\ 1\ 2\ 3\ 6\ 4$。
加粗标记的是这次操作影响到的数。
数据范围:
对于$40\%$的数据,$1\leqslant n,m\leqslant 200$。
对于$60\%$的数据,$1\leqslant n,m\leqslant 2,000$。
对于$80\%$的数据,$1\leqslant n,m\leqslant 20,000$。
对于$100\%$的数据,$1\leqslant n,m\leqslant 200,000,1\leqslant a_i\leqslant n,1\leqslant j_k\leqslant n$。
题解
$\Theta(n\log n)$求逆序对在此不再赘述。
打开大样例,发现很多都是一样的,于是考虑乱搞……
发现,如果在前面某一次操作时动了某个位置,如果现在还动这个位置则对逆序对个数是没有影响的(相当与没动),直接输出上一次的结果即可。
时间复杂度:$\Theta(n^2\log n)$。
期望得分:$60$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[200001],top;
int e[200001],b[200001];
int tr[200010];
bool vis[200001];
long long ans;
int lowbit(int x){return x&-x;}
void add(int x){for(int i=x;i;i-=lowbit(i))tr[i]++;}
int ask(int x){int res=0;for(int i=x;i<=n+1;i+=lowbit(i))res+=tr[i];return res;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){ans+=ask(a[i]+1);add(a[i]);}
printf("%lld ",ans);
while(m--)
{
int k;
scanf("%d",&k);
if(vis[k]){printf("%lld ",ans);continue;}
if(!ans){puts("0");continue;}
ans=top=0;
for(int i=1;i<=n;i++)tr[i]=0;
for(int i=k;i<=n;i++)
if(a[i]<=a[k])
{
vis[i]=1;
e[++top]=a[i];
b[top]=i;
}
sort(e+1,e+top+1);
for(int i=1;i<=top;i++)a[b[i]]=e[i];
for(int i=1;i<=n;i++){ans+=ask(a[i]+1);add(a[i]);}
printf("%lld ",ans);
}
return 0;
}
rp++