您的位置:首页 > 其它

【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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: