【cqoi2011】动态逆序对
2017-03-31 20:20
162 查看
时间限制:1秒 内存限制:64M
【问题描述】
对于序列A[i],它的逆序对数定义为满足:i < j,且A[i] > A[j]的数对(i,j)的个数。
给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
【输入格式】
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
【输出格式】
输出包含m行,依次为删除每个元素之前,逆序对的个数。
【输入样例】
5 4
1
5
3
4
2
5
1
4
2
【输出样例】
5
2
2
1
【数据范围】
编号 1-2 3-4 5-6 7-8 9-10
n <=1000 <=30000 <=50000 <=60000 <=100000
m <=100 <=10000 <=20000 <=40000 <=50000
【来源】
(1,5,3,4,2) -> (1,3,4,2) -> (3,4,2) -> (3,2) -> (3)。
今天晚上一直在做这道题,做了几种方法出来,但时间复杂度各不相同,准确的说只是常数不同,但就是这个常数有2种方法看起来可以却要被卡住1~2个点。
第一种方法:树套树,常数极高,可能是因为我用的线段树套平衡树,线段树的常数大了点。
第二种:分块+排序,普通的分块方法,就按照要求,分成几块,然后块内排序。删除就找前面比他大的和后面比他小的就好。会卡常数一个点。
第三种:分块+bit,也是分块,而且这种方法也要优化才能过,用bit记录块内的值,bit[i][j]前i个块一共有多少个在j所属的范围内。这个前缀和的思想可以省很多时间。
第四种:CDQ分治,也要优化,优化了特别快,没有优化特别慢,最可怕的是不容易看出来优化在哪里。
第五种:持续化线段树:思想和分块加bit的优化版差不多。
这里我提供一下CDQ分治的代码(因为难想),一个优化了的,一个没优化的,其他代码就不给了,请读者自己好好写,都不难但是请主要细节。
#include<cstdlib> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn=1000005; struct shu { int x,id,y; ll ans; friend bool operator <(shu a,shu b) { return a.y<b.y; } }a[maxn],c[maxn]; int n,m,bit[maxn],b[maxn]; ll lowbit(ll x){ return x&(-x); } void in(ll x){ for(;x<=n;x+=lowbit(x)) bit[x]++; } void out(ll x){ for(;x<=n;x+=lowbit(x)) bit[x]=0; } ll find(ll x){ ll t=0; for(;x>=1;x-=lowbit(x)) t+=bit[x]; return t; } bool cmp(shu a,shu b){ return a.id<b.id; } /*这是没优化的 void work(int l,int r){ if(l==r) return; int m=(l+r)>>1; work(l,m); work(m+1,r); sort(a+l,a+m+1); sort(a+m+1,a+r+1); int j=l; for(int i=m+1;i<=r;i++) { while(j<=m&&a[j].y<a[i].y) { in(n+1-a[j].x); j++; } a[i].ans+=find(n-a[i].x); } while(j>=l) { out(n+1-a[j].x); j--; } j=m; for(int i=r;i>m;i--) { while(j>=l&&a[j].y>a[i].y) { in(a[j].x); j--; } a[i].ans+=find(a[i].x-1); } while(j<=m) { out(a[j].x); j++; } } */ //这是优化了的,请自行对比。(可以复制下来试试速度,保证长见识) void work(int l,int r){ if(l==r) return; int m=(l+r)>>1; work(l,m); work(m+1,r); sort(a+l,a+r+1); int j=l; for(int i=l;i<=r;i++) { if(a[i].id<=m) in(n+1-a[i].x); else a[i].ans+=find(n-a[i].x); } for(int i=l;i<=r;i++) if(a[i].id<=m) out(n+1-a[i].x); j=m; for(int i=r;i>=l;i--) { if(a[i].id<=m) in(a[i].x); else a[i].ans+=find(a[i].x-1); } for(int i=l;i<=r;i++) if(a[i].id<=m) out(a[i].x); } int read(){ int x=0; bool ok=0; char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); while((ch>='0'&&ch<='9')||ch=='-') { if(ch=='-') ok=1; else x=x*10+ch-'0'; ch=getchar(); } return ok?-x:x; } char ch[50]; void out2(ll x){ int len=0; if(!x) putchar('0'); while(x) { ch[len++]=x%10+'0'; x/=10; } for(int i=len-1;i>=0;i--) putchar(ch[i]); putchar('\n'); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); n=read();m=read(); for(int i=1;i<=n;i++) { a[i].x=read(); a[i].ans=0; a[i].y=i; a[i].id=0; b[a[i].x]=i; } int x,t=0; for(int i=1;i<=m;i++) { x=read(); a[b[x]].id=n+1-i; } for(int i=1;i<=n;i++) if(a[i].id==0) a[i].id=++t; sort(a+1,a+1+n,cmp); work(1,n); sort(a+1,a+1+n,cmp); ll ans=0; for(int i=1;i<=n;i++) ans+=a[i].ans; for(int i=n;i>=n+1-m;i--) { out2(ans); ans-=a[i].ans; } return 0; }
相关文章推荐
- bzoj 3295: [Cqoi2011]动态逆序对(树套树)
- bzoj3295: [Cqoi2011]动态逆序对(题解2)
- [bzoj3295][Cqoi2011][动态逆序对] (树套树)
- bzoj3295 [Cqoi2011]动态逆序对(CDQ分治)
- bzoj 3295: [Cqoi2011]动态逆序对(树套树 or CDQ分治)
- BZOJ3295: [Cqoi2011]动态逆序对 莫队
- [BZOJ3295][Cqoi2011]动态逆序对(分块重建)
- BZOJ 3295: [Cqoi2011]动态逆序对 分块大法好
- [BZOJ3295] [Cqoi2011]动态逆序对 (树套树)or(CDQ分治)
- NKOJ 2041 (CQOI 2011)动态逆序对 (CDQ分治+树状数组/树套树)
- 【BZOJ3295】动态逆序对(CQOI2011)-CDQ分治:三维偏序
- BZOJ 3295 [Cqoi2011]动态逆序对 - 树状数组套主席树/树状数组套treap
- bzoj 3295 [Cqoi2011]动态逆序对(cdq分治,BIT)
- bzoj3295 [Cqoi2011]动态逆序对(cdq分治+树状数组)
- bzoj 3295: [Cqoi2011]动态逆序对 (CDQ分治+树状数组)
- BZOJ3295:[Cqoi2011]动态逆序对 (BIT套treap/CDQ分治+BIT)
- [BZOJ3295][Cqoi2011]动态逆序对(CDQ分治||树套树)
- COGS-1715 [CQOI2011]动态逆序对(树状数组+线段树)
- 【BZOJ】【3295】【CQOI2011】动态逆序对
- BZOJ3295: [Cqoi2011]动态逆序对(洛谷P3157)