hdu_5193_Go to movies Ⅱ(带插入删除的逆序对,块状链表)

题目链接:hdu_5193_Go to movies Ⅱ

题意:

有n个人站成一排,每个人的身高为Hi。每次有人加入或者有人离开,就要判断有多少人站反了(i < j&&Hi>Hj) 
第一行n,m,接下来n个整数(n,m<=2e4) 
接下来m行, 
0 x y 表示有一个身高为y的人插在x后面,x=0表示插在最前面。(1≤y≤n) 
1 x 表示第x个人(从左到右)离开。

题解:

官方题解:

添加或者删除一个元素时,维护逆序对时,需要知道在它之前有多少个数比它大,在它之后有多少个数比他小。有下标和权值两个维度,可以使用两个数据结构嵌套。题目中n=20000,范围不大,外层可以使用分块维护下标,这样添加和删除元素的时候, 
也很方便,直接暴力。查找权值个数时,使用树状数组比较方便。内层通过树状数组维护权值。设每块的大小为S,那么删除或者添加元素时,维护逆序对数的复杂度是O(S+P∗logn),S是块内直接暴力更新逆序对的代价, 
​P/S∗logn在前面块找比它大和在后面块中找比它小的代价,P表示当前元素的个数。为了使这两部分复杂度尽量均摊让S=P/S∗logn,S取根号Plogn 
​。直接通过分块暴力添加和删除时,块的大小会退化,需要重构,。重构一次的复杂度是S*logn, 总的重构复杂度是,与添加和删除总的复杂度一至。因此整个问题的复杂度为O(m√nlogn).

  1 #include<bits/stdc++.h>
  2 #define F(i,a,b) for(int i=a;i<=b;i++)
  3 using namespace std;
  4 
  5 const int N=20010,sqr=800;
  6 int ans,tot,n,flag,op,x,y,m;
  7 
  8 inline void add(int *c,int x,int v){while(x<=n)c[x]+=v,x+=x&-x;}
  9 
 10 inline int sum(int *c,int x){int an=0;while(x>0)an+=c[x],x-=(x&-x);return an;}
 11 
 12 struct node
 13 {
 14     int dt[sqr+10],sz,c[N],nxt,pre;
 15     void init(){memset(c,0,sizeof(c)),sz=0,pre=0,nxt=0;}
 16 }b[400];
 17 
 18 int getpos(int &x)
 19 {
 20     int i=1;
 21     while(b[i].sz<x&&b[i].nxt)x-=b[i].sz,i=b[i].nxt;
 22     return i;
 23 }
 24 
 25 int cal(int x)
 26 {
 27     int an=0;
 28     int id=getpos(x);
 29     F(i,1,x-1)if(b[id].dt[i]>b[id].dt[x])an++;
 30     F(i,x+1,b[id].sz)if(b[id].dt[i]<b[id].dt[x])an++; 
 31     for(int i=1;i!=id;i=b[i].nxt)
 32         an+=sum(b[i].c,n)-sum(b[i].c,b[id].dt[x]);
 33     for(int i=b[id].nxt;i!=0;i=b[i].nxt)
 34         an+=sum(b[i].c,b[id].dt[x]-1);
 35     return an;
 36 }
 37 
 38 void del(int x){
 39     ans-=cal(x);
 40     int id=getpos(x);
 41     add(b[id].c,b[id].dt[x],-1);
 42     F(i,x+1,b[id].sz)b[id].dt[i-1]=b[id].dt[i];     
 43     b[id].sz--;
 44     if(b[id].sz==0)//删除块
 45     {      
 46         int pre=b[id].pre,net=b[id].nxt;
 47         b[net].pre=pre,b[pre].nxt=net;
 48     }
 49 }
 50 
 51 void ins(int x,int y)
 52 {
 53     int xx=x;
 54     if(flag==0)
 55     {
 56         b[1].init(),b[1].dt[++b[1].sz]=y;
 57         add(b[1].c,b[1].dt[1],1),flag=1;
 58         return;
 59     }
 60     int id=getpos(x);//在第几个块
 61     if(b[id].sz==sqr)//块分裂
 62     {
 63         b[++tot].init(),b[id].sz++;
 64         int cnt=0;
 65         for(int i=b[id].sz;i>x;i--)
 66             b[id].dt[i]=b[id].dt[i-1];
 67         b[id].dt[x]=y;
 68         add(b[id].c,b[id].dt[x],1);
 69         int len=b[id].sz/2;
 70         F(i,len+1,b[id].sz)
 71         {
 72             b[tot].dt[++cnt]=b[id].dt[i];
 73             add(b[tot].c,b[id].dt[i],1);
 74             add(b[id].c,b[id].dt[i],-1);
 75         }
 76         b[tot].sz=cnt,b[tot].nxt=b[id].nxt;
 77         b[id].nxt=tot,b[id].sz=len,b[tot].pre=id; 
 78     }
 79     else{
 80         b[id].sz++;
 81         for(int i=b[id].sz;i>x;i--)
 82             b[id].dt[i]=b[id].dt[i-1];
 83         b[id].dt[x]=y;
 84         add(b[id].c,b[id].dt[x],1);
 85     }
 86     ans+=cal(xx);
 87 }
 88 
 89 int main(){
 90     while(~scanf("%d%d",&n,&m))
 91     {
 92         ans=0,tot=1,b[1].sz=0,flag=0;
 93         F(i,1,n)scanf("%d",&y),ins(i,y);
 94         F(i,1,m)
 95         {
 96             scanf("%d",&op);
 97             if(op==0)scanf("%d%d",&x,&y),ins(x+1,y);
 98             else scanf("%d",&x),del(x);
 99             printf("%d\n",ans);
100         }
101     }
102     return 0;
103 }
View Code

 

转载于:https://www.cnblogs.com/bin-gege/p/6040685.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值