洛谷P1908 逆序对 [权值线段树]

  题目传送门

逆序对

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。

输入输出格式

输入格式:

 

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。

 

输出格式:

 

给定序列中逆序对的数目。

 

输入输出样例

输入样例#1: 复制
6
5 4 2 6 3 1
输出样例#1: 复制
11

说明

对于50%的数据,n≤2500

对于100%的数据,n≤40000。


  分析:

  用归并或者树状数组都可以轻易A掉,但是这里用权值线段树。可以算是一道权值线段树的模板题。用于理解权值线段树。

  Code:

  

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn=1e5+7;
 5 int n,p[maxn];ll ans;
 6 struct Num{
 7     int id,val;
 8 }a[maxn];
 9 struct Seg{
10     int l,r;ll val;
11 }seg[maxn<<2];
12 inline void pushup(int rt)
13 {
14     seg[rt].val=seg[rt<<1].val+seg[rt<<1|1].val;
15 }
16 inline void build(int l,int r,int rt)
17 {
18     seg[rt].l=l,seg[rt].r=r;
19     if(l==r){seg[rt].val=0;return;}
20     int mid=(l+r)>>1;
21     build(l,mid,rt<<1);
22     build(mid+1,r,rt<<1|1);
23 }
24 inline void update(int rt,int x)
25 {
26     if(seg[rt].l==seg[rt].r&&seg[rt].l==x){
27         seg[rt].val++;return;}
28     int mid=(seg[rt].l+seg[rt].r)>>1;
29     if(x<=mid)update(rt<<1,x);
30     if(x>mid)update(rt<<1|1,x);
31     pushup(rt);
32 }
33 inline ll quary(int rt,int l,int r)
34 {
35     if(seg[rt].l>r||seg[rt].r<l)return 0;
36     if(l<=seg[rt].l&&seg[rt].r<=r)
37         return seg[rt].val;
38     int mid=(seg[rt].l+seg[rt].r)>>1;int ret=0;
39     if(l<=mid)ret+=quary(rt<<1,l,r);
40     if(r>mid)ret+=quary(rt<<1|1,l,r);
41     return ret;
42 }
43 bool cmp(Num x,Num y)
44 {return x.val<y.val;}
45 int main()
46 {
47     ios::sync_with_stdio(false);
48     cin>>n;
49     build(1,maxn,1);
50     for(int i=1;i<=n;i++)
51         cin>>a[i].val,a[i].id=i;
52     sort(a+1,a+n+1,cmp);
53     for(int i=1;i<=n;i++)
54          p[a[i].id]=i;
55     for(int i=1;i<=n;i++){
56         ans+=quary(1,p[i]+1,maxn);
57         update(1,p[i]);}
58     cout<<ans<<"\n";
59     return 0;
60 } 

猜你喜欢

转载自www.cnblogs.com/cytus/p/9226758.html