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

本文介绍了如何使用树状数组结合Treap数据结构来解决动态逆序对的问题,主要针对ACM竞赛中的题目hdu4988——Little Pony and Boast Busters进行解析。
摘要由CSDN通过智能技术生成

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, I can do better. Any takers? Anyone? Or is Trixie destined to be the greatest equine who has ever lived!?!" — "Boast Busters"

Given two permutation P 0 && P 1 of {0, 1, ..., n - 1}, we define the crossing number of it as follows. Write P 0 from left to right above P 1 and draw a straight line between each same elements. The crossing number of P 0 and P 1 is the number of pairs of lines that cross.

For example, if n = 5, and P 0 = {0, 1, 2, 3, 4}, and P 1 = {1, 3, 0, 2, 4}, then the crossing number of P 0 and P 1 is 3, as shown in the figure below.


Now given you the two permutation, you need to implement the following operations:

SWAP p a b: swap P p[a] and P p[b] (0<=p<=1, 0<=a, b<=n-1).

QUERY: ask the crossing number of the current P 0 and P 1.
 

Input
Input contains multiple test cases (less than 10). For each test case, the first line contains one integer n (1<=n<=10^5).
The second line contains n integers P 0[0], P 0[1], ..., P 0[n-1].
The third line contains n integers P 1[0], P 1[1], ..., P 1[n-1].
The next line contains one integer q —— the number of operations (1<=q<=10^5). The next q line, each line will contains a operation as we mentioned above.
 

Output
For each query, output the corresponding result in one line.
 

Sample Input
   
   
5 0 1 2 3 4 1 3 0 2 4 5 QUERY SWAP 1 2 4 QUERY SWAP 0 2 4 QUERY
 

Sample Output
   
   
3 6 5
 

Source

题意:给出两个0到n-1的排列,然后支持两种操作,QUERY查询两组排列的交叉的元素个数,SWAP 交换一个排列中两个位置的元素

思路:这题最后可以转化为给出一个排列P(将给出的两个排列的位置一一对应起来,参见大神的题解http://bestcoder.hdu.edu.cn),
           
            可以动态交换P的任意两个位置的数,然后查询整个序列的逆序对数

            动态逆序对是比较经典的树套树模型,我这里用的是树状数组套treap实现的,之前用的线段树妥妥的超时了

            可以先用树状或归并排序求出初始序列的逆序对数,记为ans,然后每次查询只需要输出ans即可

            重点在于交换,因为交换以后ans会随之改变,假设现在要交换L,R位置的数,那么[0,L-1],[R+1,n-1]这些位置的数是不用考虑的,因为它们对中间

            元素的逆序对的贡献是不会改变的,所以只用考虑[L+1,R-1]之间的数

            假设元素P[L] > P[R] ,设区间[L+1,R-1]的数小于P[L]的个数为L1,大于的个数为L2,同理设小于P[R]的个数为R1,大于的个数为R2,那么交换后的

            答案 为ans = ans + L2-L1 + R1-R2 = ans + R1+L2 - (L1+R2),现在只需求出 R1 + L2 问题即可完美解决,因为L1+R2=2*n-(R1+L2) ,其中n为区间

            [L+1,R-1]的元素个数,现在令区间[L+1,R-1]为S

            而求R1等价于求S中元素小于P[R]的个数,求L2等价于求S中元素大于P[L]的个数,所以只需要在树状数组里套一个treap,用来维护该树状数组管辖的

            区间的元素,具体涉及两种操作:

            1.交换元素,相当于先将原位置的数删除,再在新的位置插入,这个就是树状数组单点更新了

            2.查询一段区间中所有小于(大于)x的元素个数,相当于区间求和

            怎么样,难点全部攻破了吧,这题也是把我写的醉到不行,调了许久才发现竟然树状数组没有初始化,sad~,累觉不爱,可以睡觉去了

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
using namespace std;
#define INF (~0U>>1)
#define lson l,m,o<<1
#define rson m+1,r,o<<1|1
#define maxn 100010

typedef __int64 ll;

struct node{
    int v;
    int sz;
    int s;
    node *p;
    node *ch[2];

    node(){
        sz=v=0;
        s=-1;
        p=ch[0]=ch[1]=this;
    }

    bool cmp(int s)
    {
        return this->s
       
       
         p=this; } }Tnull,*null=&Tnull; node *newnode(int v) { node *u=(node*)malloc(sizeof(node)); u->sz=1; u->s=rand(); u->v=v; u->ch[0]=u->ch[1]=null; return u; } void pushup(node *o) { o->sz=o->ch[0]->sz+o->ch[1]->sz+1; } void rot(node *o,int d) { node *k=o->ch[d]; o->addc(k->ch[d^1],d); o->p->addc(k,o->p->cd(o)); k->addc(o,d^1); pushup(o); pushup(k); } void Insert(node *o,node *pre,int v,int dd) { if(o==null){ pre->addc(newnode(v),dd); return; } int d; if(v>=o->v){ d=1; Insert(o->ch[1],o,v,1); } else { d=0; Insert(o->ch[0],o,v,0); } if(o->cmp(o->ch[d]->s))rot(o,d); else pushup(o); } void Delete(node *o,int v) { if(o==null)return; if(v==o->v){ while(o->ch[0]!=null&&o->ch[1]!=null) { int d=o->ch[0]->cmp(o->ch[1]->s); rot(o,d); --o->p->sz; } node *k; o->ch[0]==null? k=o->ch[1]:k=o->ch[0]; o->p->addc(k,o->p->cd(o)); free(o); return; } v>o->v ? Delete(o->ch[1],v) : Delete(o->ch[0],v); pushup(o); } int Find(node *o,int v,int vis) { if(o==null)return 0; int ax=0; if(v>o->v){ if(!vis)ax+=o->ch[0]->sz+1; ax+=Find(o->ch[1],v,vis); } else { if(vis)ax+=o->ch[1]->sz+1; ax+=Find(o->ch[0],v,vis); } return ax; } void Free(node *o) { if(o==null)return; Free(o->ch[0]); Free(o->ch[1]); free(o); } node *tree[maxn]; int a[maxn]; int b[maxn]; int p[maxn]; int pa[maxn]; int pb[maxn]; int n; int bit(int x) { return x&(-x); } void buildST() { for(int i=0;i 
        
          ch[0]=tree[i]->ch[1]=null; } } void updateST1(int p,int v) { while(p 
         
           ch[1],tree[p],v,1); p+=bit(p+1); } } void updateST2(int p,int v) { while(p 
          
            ch[1],v); p+=bit(p+1); } } int queryST(int p,int v1,int v2) { int ax=0; while(p>=0) { ax+=Find(tree[p]->ch[1],v1,0) + Find(tree[p]->ch[1],v2,1); p-=bit(p+1); } return ax; } void FreeST() { for(int i=0;i 
           
             =0) { ans+=sum[x]; x-=bit(x+1); } return ans; } int main() { int i,Q; while(scanf("%d",&n)!=EOF) { for(i=0;i 
            
              y)swap(x,y); if(p[x]>p[y]){ y-x==1 ? temp=0 : temp=queryST(y,p[y],p[x])-queryST(x,p[y],p[x]); ans+=((temp-y+x+1)<<1)-1; } else { y-x==1 ? temp=0 : temp=queryST(y,p[x],p[y])-queryST(x,p[x],p[y]); ans+=1+((y-x-1-temp)<<1); } updateST1(x,p[y]); updateST1(y,p[x]); swap(p[x],p[y]); } } FreeST(); } return 0; } 
             
            
           
          
         
       
      
      
     
     
    
    
   
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值