bzoj2141 树状数组套Treap树

题目大意是在能够改变两个数的位置的情况下计算逆序对数

这因为是动态记录逆序对

本来单纯逆序对只要用树状数组计算即可,但这里因为更新,所以利用TReap树的删点和增加点来进行更新

大致是把每个树状数组所管理的点都放在对应的Treap树下,

这样x-=lowbit(x)下来,正好访问到是所有比他小范围下的点了

然后根据每棵访问到的Treap树有多少个节点是比当前值小的即可

每次交换ai , aj , (i<j)只要考虑(i,j)范围内比ai大的数,比aj小的数,然后加加减减即可

如果ai!=aj也是要加减1

  1 #include <bits/stdc++.h>
  2
  3 using namespace std;
  4 #define N 20005
  5
  6 struct Node{
  7     int l , r , val , sz , pri , cnt;
  8     //cnt表示当前位置相同的数有多少个,pri表示优先级,sz表示这棵子树所含有的节点总个数
  9     Node(){
 10         l = r = 0;
 11         cnt = sz = 1 , pri = rand();
 12         val = 0;
 13     }
 14     Node(int v){
 15         Node();
 16         val = v;
 17     }
 18 };
 19 #define ls node[x].l
 20 #define rs node[x].r
 21 namespace Treap{
 22     int tot;//Treap Node节点的总个数
 23     int A[N];//有n棵treap树,A[]表示每棵treap树的起始位置
 24     Node node[N*50];
 25     void init(){
 26         node[0] = Node();
 27         node[0].cnt = node[0].sz = 0;
 28         memset(A , 0 , sizeof(A));
 29         tot = 0;
 30     }
 31     int newNode(int v){
 32         ++tot;
 33         node[tot].cnt = node[tot].sz = 1;
 34         node[tot].l = node[tot].r = 0;
 35         node[tot].pri = rand();
 36         node[tot].val = v;
 37         return tot;
 38     }
 39     void push_up(int x) {
 40        // cout<<"here: "<<x<<" "<<ls<<" "<<rs<<endl;
 41         if(x>0)
 42             node[x].sz = node[ls].sz+node[rs].sz+node[x].cnt;
 43     }
 44     void rotL(int &x){
 45         int y = rs;
 46         rs = node[y].l;
 47         node[y].l = x;
 48
 49         push_up(x);
 50         push_up(y);
 51         x = y;
 52     }
 53     void rotR(int &x){
 54         int y = ls;
 55         ls = node[y].r;
 56         node[y].r = x;
 57
 58         push_up(x);
 59         push_up(y);
 60         x = y;
 61     }
 62     void insert(int &x , int v){
 63     //    cout<<x<<" "<<v<<endl;
 64         if(x == 0) x = newNode(v);
 65         else if(node[x].val>v){
 66             insert(ls , v);
 67             if(node[ls].pri>node[x].pri) rotR(x);
 68         }
 69         else if(node[x].val<v){
 70             insert(rs , v);
 71             if(node[rs].pri>node[x].pri) rotL(x);
 72         }
 73         else node[x].cnt++;
 74         push_up(x);
 75        // cout<<x<<" "<<v<<" "<<"endd"<<endl;
 76     }
 77     void erase(int &x , int v){
 78         if(x == 0) return ;
 79         else if(v<node[x].val) erase(ls , v);
 80         else if(v>node[x].val) erase(rs , v);
 81         else {
 82             node[x].cnt--;
 83             if(node[x].cnt<=0){
 84                 if(ls==0&&rs==0) x = 0;
 85                 else if(ls == 0) x = rs;
 86                 else if(rs == 0) x = ls;
 87                 else{
 88                     if(node[ls].pri <node[rs].pri) rotL(x),erase(ls,v);
 89                     else rotR(x) , erase(rs , v);
 90                 }
 91             }
 92         }
 93         push_up(x);
 94     }
 95     int getCnt(int x , int v){//从x号节点出发找到对应的子树下小于等于v的值的个数
 96         if(x == 0) return 0;
 97         int ans = 0;
 98         if(node[x].val>v) ans = getCnt(ls , v);
 99         else if(node[x].val<v) ans = node[x].cnt+node[ls].sz+getCnt(rs , v);
100         else ans = node[x].cnt+node[ls].sz;
101         return ans;
102     }
103 }
104 int n , m , h[N] , a[N] , tot;
105 //树状数组部分
106 #define lowbit(x) x&(-x)
107 void Add(int id , int v)
108 {
109     for(int x=id ; x<=n ; x+=lowbit(x)){
110         Treap::insert(Treap::A[x] , v);
111     }
112 }
113 void Erase(int id , int v)
114 {
115     for(int x=id ; x<=n ; x+=lowbit(x)){
116         Treap::erase(Treap::A[x] , v);
117     }
118 }
119 int getSum(int p , int v)//cal 1~p区间内小于等于v的值的个数
120 {
121     int sum = 0;
122     for(int x=p ; x>0 ; x-=lowbit(x)){
123         sum+=Treap::getCnt(Treap::A[x] , v);
124     }
125     return sum;
126 }
127 int getSumMin(int p , int v)//cal 1~p区间内小于v的值的个数
128 {
129     v--;//important保证等于的情况被排除
130     return getSum(p , v);
131 }
132
133 int main()
134 {
135    // freopen("a.in" , "r" , stdin);
136     scanf("%d" , &n);
137     for(int i=1 ; i<=n ; i++){
138         scanf("%d" , &h[i]);
139         a[i] =  h[i];
140     }
141     sort(a+1 , a+n+1);
142     tot = unique(a+1 , a+n+1)-(a+1);
143     Treap::init();
144     int sum = 0;
145     for(int i=1 ; i<=n ; i++){
146         h[i] = lower_bound(a+1 , a+tot+1 , h[i])-a;
147         Add(i , h[i]);
148         sum+=i-1-getSum(i-1 , h[i]);
149     }
150     printf("%d\n" , sum);
151
152     scanf("%d" , &m);
153     while(m--){
154 //        for(int i=1 ; i<=n ; i++)
155 //            cout<<h[i]<<" ";
156 //        cout<<endl;
157         int ai , bi;
158         scanf("%d%d" , &ai , &bi);
159         if(ai>bi) swap(ai , bi);
160         if(h[ai]<h[bi]) sum++;
161         else if(h[ai]>h[bi]) sum--;
162         else{
163             printf("%d\n" , sum);
164             continue;
165         }
166         int add = 0;
167         if(bi-ai>1){
168             add += getSumMin(bi-1 , h[bi])-getSumMin(ai , h[bi]);
169             add -= (bi-ai-1)-(getSum(bi-1 , h[bi])-getSum(ai , h[bi]));
170             add += (bi-ai-1)-(getSum(bi-1 , h[ai])-getSum(ai , h[ai]));
171             add -= getSumMin(bi-1 , h[ai])-getSumMin(ai , h[ai]);
172         }
173
174         sum += add;
175
176         Erase(ai , h[ai]);
177         Erase(bi , h[bi]);
178         Add(ai , h[bi]);
179         Add(bi , h[ai]);
180         swap(h[ai] , h[bi]);
181         printf("%d\n" , sum);
182     }
183     return 0;
184 }
时间: 2025-01-01 03:25:21

bzoj2141 树状数组套Treap树的相关文章

Bestcoder7(1004)hdu4988(经典问题:树状数组套treap求解动态逆序对)

Little Pony and Boast Busters Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 83    Accepted Submission(s): 32 Problem Description "I hereby challenge you, Ponyvillians: anything you can do

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

BZOJ 3196 Tyvj 1730 二逼平衡树 ——树状数组套主席树

[题目分析] 听说是树套树.(雾) 怒写树状数组套主席树,然后就Rank1了.23333 单点修改,区间查询+k大数查询=树状数组套主席树. [代码] #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <set> #include <map> #include <string> #include <alg

[BZOJ 3196] 二逼平衡树 树状数组套主席树

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3357  Solved: 1326[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为

[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过128MB = = 嗯就是这样,代码长度= =我写了260行......Debug了n小时= = 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #in

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin

BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接 方法:树状数组套线段树 解析: 这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队.这道题比那道更要简单. 然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy.引用理论部分. 删除某个数,只要统计它之前还存在的比它大的

【BZOJ1901】Dynamic Rankings,树状数组套主席树

Time:2016.05.09 Author:xiaoyimi 转载注明出处谢谢 传送门(权限) 题面 1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec Memory Limit: 128 MB Submit: 6678 Solved: 2777 [Submit][Status][Discuss] Description 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a

[COGS257]动态排名系统 树状数组套主席树

257. 动态排名系统 时间限制:5 s   内存限制:512 MB [问题描述]给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:1.查询A[i],A[i+1],A[i+2],...,A[j](1<=i<=j<=N)中,升序排列后排名第k的数.2.修改A[i]的值为j.所谓排名第k,指一些数按照升序排列后,第k位的数.例如序列{6,1,9,6,6},排名第3的数是6,排名第5的数是9.[输入格式]第一行包含一个整数D(0<=