题解 P6278 【[USACO20OPEN]Haircut G】

楼上dalao解法非常清晰。蒟蒻的解法大致相同,但对于细节有些不同的处理。

为什么能够用线段树/树状数组?

我们知道,一个数产生的逆序对取决于他前面比他大的数的个数。

于是,题目转化为,每输入一个数,就将这个数对于逆序对的贡献值增加他前面比他大的数的个数,然后将这个数存下来。

答案的统计方法:

当头发长度为 (A_i) 时逆序对的数量 (=)(A_i) 之前的逆序对数量 (+) 这个数所能贡献的逆序对的数量。

这个操作用树状数组会非常好实现,但是我树状数组不是特别熟,于是在比赛上选择了打线段树。

本来应该int能过,但懒得调,于是直接换成了long long

#include <iostream>
#include <algorithm>

using namespace std;
const long long MAXN = 1e5+5;
long long n,pos[MAXN],num[MAXN],seg[8*MAXN],pre;
#define lson (way<<1)
#define rson ((way<<1)+1)
#define mid (l+r)/2
void setIO(string name) {
	freopen((name+".in").c_str(),"r",stdin);
	freopen((name+".out").c_str(),"w",stdout);
	ios_base::sync_with_stdio(0);
}

inline long long query(long long way, long long l, long long r ,long long qlow, long long qhigh){

  if (qlow<=l && r<=qhigh) return seg[way];
  if (l>qhigh || r<qlow) return 0;
  return (query(lson,l,mid,qlow,qhigh)+query(rson,mid+1,r,qlow,qhigh));
}//询问 Ai+1 到 n ,也就是比 Ai 大的数的数量
inline void update(long long way, long long l, long long r, long long qlow){
  if (l==r && l==qlow) {seg[way]++;return;}
  if (mid>=qlow) update(lson,l,mid,qlow);
  else update(rson,mid+1,r,qlow);
  seg[way]++;
}//更新Ai的数量
int main(){
  setIO("haircut");
  cin >> n;
  for (long long i=0;i<n;i++) {
    cin >> pos[i];
    if (pos[i]+1<=n) num[pos[i]]+=query(1,0,n,pos[i]+1,n);//询问
    update(1,0,n,pos[i]);//更新
  }
  cout << 0 << endl;
  for (long long i=1;i<n;i++){
    pre+=num[i-1];//先将这个数的贡献加上
    cout << pre << endl;//然后输出
  }
}
原文地址:https://www.cnblogs.com/DannyXu/p/12636846.html