【cqoi2011】动态逆序对

来源:互联网 发布:win10缺少—个网络协议 编辑:程序博客网 时间:2024/06/03 09:13
时间限制: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;   }
1 0
原创粉丝点击