Hdu 5193 Go to movies Ⅱ(带删除数插入数的逆序数对,块状链表)
2016-09-14 22:47
429 查看
传送门: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个人(从左到右)离开。
思路:摘自题解http://bestcoder.hdu.edu.cn/solutions.php?page=12
添加或者删除一个元素时,维护逆序对时,需要知道在它之前有多少个数比它大,在它之后有多少个数比他小。有下标和权值两个维度,可以使用两个数据结构嵌套。题目中n=20000,范围不大,外层可以使用分块维护下标,这样添加和删除元素的时候,
也很方便,直接暴力。查找权值个数时,使用树状数组比较方便。内层通过树状数组维护权值。设每块的大小为S,那么删除或者添加元素时,维护逆序对数的复杂度是O(S+P∗logn),S是块内直接暴力更新逆序对的代价,
P/S∗logn在前面块找比它大和在后面块中找比它小的代价,P表示当前元素的个数。为了使这两部分复杂度尽量均摊让S=P/S∗logn,S取根号Plogn
。直接通过分块暴力添加和删除时,块的大小会退化,需要重构,。重构一次的复杂度是S*logn, 总的重构复杂度是,与添加和删除总的复杂度一至。因此整个问题的复杂度为O(m√nlogn).
题意:
有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个人(从左到右)离开。
思路:摘自题解http://bestcoder.hdu.edu.cn/solutions.php?page=12
添加或者删除一个元素时,维护逆序对时,需要知道在它之前有多少个数比它大,在它之后有多少个数比他小。有下标和权值两个维度,可以使用两个数据结构嵌套。题目中n=20000,范围不大,外层可以使用分块维护下标,这样添加和删除元素的时候,
也很方便,直接暴力。查找权值个数时,使用树状数组比较方便。内层通过树状数组维护权值。设每块的大小为S,那么删除或者添加元素时,维护逆序对数的复杂度是O(S+P∗logn),S是块内直接暴力更新逆序对的代价,
P/S∗logn在前面块找比它大和在后面块中找比它小的代价,P表示当前元素的个数。为了使这两部分复杂度尽量均摊让S=P/S∗logn,S取根号Plogn
。直接通过分块暴力添加和删除时,块的大小会退化,需要重构,。重构一次的复杂度是S*logn, 总的重构复杂度是,与添加和删除总的复杂度一至。因此整个问题的复杂度为O(m√nlogn).
#include<bits/stdc++.h> using namespace std; const int maxn=20010; const int MAX_S=1050; int ans,tot,n; void add(int *c,int x,int v){ while(x<=n){ c[x]+=v; x+=(x&-x); } } int sum(int *c,int x){ int ret=0; while(x>0){ ret+=c[x]; x-=(x&-x); } return ret; } struct node_t{ int num[MAX_S+10],sz,c[maxn],next,pre; void init(){ memset(c,0,sizeof(c)); sz=0; pre=0; next=0; } }b[310]; int get_pos(int &x){ int i=1; while(b[i].sz<x&&b[i].next) x-=b[i].sz,i=b[i].next; return i; } int cal(int x){ int ret=0; int id=get_pos(x); for(int i=1;i<x;i++) if(b[id].num[i]>b[id].num[x]) ret++; for(int i=x+1;i<=b[id].sz;i++) if(b[id].num[i]<b[id].num[x]) ret++; for(int i=1;i!=id;i=b[i].next) ret+=sum(b[i].c,n)-sum(b[i].c,b[id].num[x]); for(int i=b[id].next;i!=0;i=b[i].next) ret+=sum(b[i].c,b[id].num[x]-1); return ret; } int flag; void ins(int x){ int y=x; if(flag==0){ //是不是第一个元素 b[1].init(); scanf("%d",&b[1].num[++b[1].sz]); add(b[1].c,b[1].num[1],1); flag=1; return ; } int id=get_pos(x); //id表示在哪一块中 if(b[id].sz==MAX_S){ //块重构 b[++tot].init(),b[id].sz++; int cnt=0; for(int i=b[id].sz;i>x;i--) b[id].num[i]=b[id].num[i-1]; scanf("%d",&b[id].num[x]); add(b[id].c,b[id].num[x],1); int len=b[id].sz/2; for(int i=len+1;i<=b[id].sz;i++){ b[tot].num[++cnt]=b[id].num[i]; add(b[tot].c,b[id].num[i],1); add(b[id].c,b[id].num[i],-1); } b[tot].sz=cnt,b[tot].next=b[id].next; b[id].next=tot,b[id].sz=len; b[tot].pre=id; } else{ b[id].sz++; for(int i=b[id].sz;i>x;i--) b[id].num[i]=b[id].num[i-1]; scanf("%d",&b[id].num[x]); add(b[id].c,b[id].num[x],1); } ans+=cal(y); } void del(int x){ ans-=cal(x); int id=get_pos(x); add(b[id].c,b[id].num[x],-1); for(int i=x+1;i<=b[id].sz;i++) b[id].num[i-1]=b[id].num[i]; b[id].sz--; if(b[id].sz==0){ //删除块 int pre=b[id].pre,net=b[id].next; b[net].pre=pre; b[pre].next=net; } } int main(){ int m,op,x,y; while(~scanf("%d%d",&n,&m)){ ans=0,tot=1,b[1].sz=0,flag=0; for(int i=1;i<=n;i++) ins(i); for(int i=1;i<=m;i++){ scanf("%d",&op); if(op==0){ scanf("%d",&x); ins(x+1); } else{ scanf("%d",&x); del(x); } printf("%d\n",ans); } } return 0; }
相关文章推荐
- hdu_5193_Go to movies Ⅱ(带插入删除的逆序对,块状链表)
- HDU 5193 Go to movies Ⅱ 块状链表套树状数组(带插入和删除的逆序对)
- HDU 5193 块状链表求带插入删除操作的全局逆序对数
- HDU 5193 Go to movies Ⅱ(块状数组套树状数组)
- HDU 5193 Go to movies Ⅱ 块状链表套树状数组
- 双向循环链表的插入和删除
- 单链表操作(建表、插入、删除、查找、求表长),
- 单链表的插入和删除(C++)
- 再论循环链表的插入和删除操作
- 双向非循环递增链表——插入,删除,清空
- 单链表的初始化,建立,插入,查找,删除。
- 【回忆c语言】从指针的定义初始化到链表的插入删除排序实现code
- 双向非循环递增链表——插入,删除,清空
- 双链表的初始化,建立,插入,查找,删除。
- 再写链表的建立、浏览、修改、插入、删除(接下来是双链表)
- 链表的相关操作查找插入删除(c++ 数据结构)
- 链表的插入,删除,逆置操作...
- 链表的生成、插入、删除(LinkList)
- 用C语言编写一个包含链表的初始化、插入、删除、查找等基本操作的程序。
- 单链表相关操作之C语言实现:插入,删除,倒转,复制,查找。。。