卧薪尝胆,厚积薄发。
排序
Date: Wed Oct 31 19:25:49 CST 2018
In Category:
NoCategory
Description:
$n$
个人排成一排,每个人有身高,每次将所有身高小于等于
$k$
的人按顺序排列,询问序列逆序对数。
$1\leqslant n\leqslant300000$
Solution:
先求出
$val[i]$
表示在身高第
$i$
的人和小于等于他的数形成了多少逆序对,这个可以把所有人按身高从小到大排序,每次在树状数组中下标位置加一,注意同样的数要按位置从大到小排序,发现每次就是去掉所有
$\leqslant k$
的之间的逆序对数,保留
$>k$
的之间的和
$\leqslant k$
和
$>k$
的逆序对,于是把
$val[i]$
做个前缀和
$ans[]$
,最开始的逆序对就是
$ans[n]$
,然后对于每次询问,如果有更多的逆序对被去掉了,就不停减
$val[cur]$
直到
$a[s[cur]]>k$
,因为
$val[i]$
的含义是他和比他小的,所以这样做恰好削掉了所有
$\leqslant k$
的数之间的逆序对。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,m;
#define MAXN 300010
int a[MAXN];
int c[MAXN];
int lowbit(int x){return x & (-x);}
void add(int p,int x){for(int i = p;i <= n;i += lowbit(i))c[i] += x;return;}
int query(int p){int res = 0;for(int i = p;i >= 1;i -= lowbit(i))res += c[i];return res;}
int s[MAXN];
bool cmp_a(int x,int y){return (a[x] == a[y] ? x > y : a[x] < a[y]);}
int val[MAXN];
long long ans[MAXN];
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)s[i] = i;
sort(s + 1,s + 1 + n,cmp_a);
for(int i = 1;i <= n;++i)
{
val[i] = query(n) - query(s[i]);
add(s[i],1);
ans[i] = ans[i - 1] + val[i];
}
int k;
int cur = 1;
long long res = ans[n];
cout << res << endl;
for(int i = 1;i <= m;++i)
{
scanf("%d",&k);k = a[k];
while(cur <= n && k >= a[s[cur]])
{
res -= val[cur];
++cur;
}
printf("%lld\n",res);
}
return 0;
}
In tag:
数据结构-树状数组
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡