• BZOJ3295 CQOI2011 动态逆序对


    3295: [Cqoi2011]动态逆序对

    Time Limit: 10 Sec  Memory Limit: 128 MB

    Description

    对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。
    给1到n的一个排列,按照某种顺序依次删除m个元素。
    你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

    Input

    输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。
    以下n行每行包含一个1到n之间的正整数,即初始排列。
    以下m行每行一个正整数,依次为每次删除的元素。

    Output 

    输出包含m行,依次为删除每个元素之前,逆序对的个数。

    Sample Input

    5 4

    1 5 3 4 2

    5 1 4 2

    Sample Output

    5
    2
    2
    1
    样例解释
    (1,5,3,4,2) (1,3,4,2) (3,4,2) (3,2) (3)。

    HINT 

    N<=100000 M<=50000

      表示当时没看出来是CDQ,树套树倒是瞄出来了,只是不会写啊。

      现在看来还是很显然的。

      对于排列中的数,记它被删除的时间为t(未删除的设为m+1),下标为X,大小为Y。

      那么对于一个数i,来看在它前面被删除的数对它的答案的减小值为多少:

        Σ(Tj<Ti && Xj<Xi && Yj>Yi) + Σ(Tj<Ti && Xj>Xi && Yj<Yi)

      做两次CDQ就好了。这种没有重复的算起来真是爽啊。

      做的时候没有草稿纸真TM不爽。

    #include    <iostream>
    #include    <cstdio>
    #include    <cstdlib>
    #include    <algorithm>
    #include    <vector>
    #include    <cstring> 
    using namespace std;
     
    const int N = 100010;
    struct Data{
        int X,Y,T;long long len;
        bool operator <(const Data &t)const{
            return T<t.T;
        }
    }rem[N],f[N],t[N];
    int n,m,ban[N],bplace[N],A[N],T[N];
    long long Ans,ans[N];
     
    inline int gi()
    {
        int x=0,res=1;char ch=getchar();
        while(ch>'9' || ch<'0'){if(ch=='-')res=-res;ch=getchar();}
        while(ch>'/' && ch<':')x=x*10+ch-48,ch=getchar();
        return x*res;
    }
     
    inline int lb(int k){return k&-k;}
     
    inline void update(int x){for(;x<=n;x+=lb(x))T[x]++;}
     
    inline int query(int x)
    {
        int ans=0;
        for(;x;x-=lb(x))ans+=T[x];
        return ans;
    }
     
    inline void clean(int x){for(;x<=n;x+=lb(x))T[x]=0;}
     
    inline void calc()
    {
        memset(T,0,sizeof(T));Ans=0;
        for(int i=1;i<=n;++i){
            Ans+=(i-query(A[i])-1);
            update(A[i]);
        }
        memset(T,0,sizeof(T));
    }
    //在外面保证T有序,CDQ内保证X有序,统计Y值。 
    //T:时间 X:位置 Y:权值
    //CDQ calc Ti<Tj Xi<Xj Y[i]<Y[j]
    inline void CDQ(int l,int r)
    {
        if(l==r)return;int mid=(l+r)>>1;
        CDQ(l,mid);CDQ(mid+1,r);
        int x=l,y=mid+1,z=l;
        while(x<=mid && y<=r)
            if(f[x].X<f[y].X)update(f[x].Y),t[z++]=f[x++];
            else f[y].len+=query(f[y].Y),t[z++]=f[y++];
        while(x<=mid)t[z++]=f[x++];
        while(y<=r)f[y].len+=query(f[y].Y),t[z++]=f[y++];
        for(int i=l;i<=mid;++i)clean(f[i].Y);
        for(int i=l;i<=r;++i)f[i]=t[i];
    }
     
    int main()
    {
        n=gi();m=gi();
        for(int i=1;i<=n;++i)
            A[i]=gi();
        for(int i=1;i<=m;++i)
            bplace[ban[i]=gi()]=i;
        for(int i=1;i<=n;++i){
            rem[i].T=bplace[A[i]];
            rem[i].X=i;
            rem[i].Y=A[i];
            if(!rem[i].T)rem[i].T=m+1;
        }
        sort(rem+1,rem+n+1);calc();
        for(int i=0;i<=m;++i)ans[i]=Ans;
        for(int i=1;i<=n;++i){
            f[i]=rem[i];
            f[i].Y=n-f[i].Y+1;
        }
        reverse(f+1,f+n+1);
        CDQ(1,n);sort(f+1,f+n+1);
        for(long long i=1,s=0;i<=n;++i){
            if(!f[i].T)continue;
            ans[f[i].T]-=s;s+=f[i].len;
        }
        for(int i=1;i<=n;++i){
            f[i]=rem[i];
            f[i].X=n-f[i].X+1;
        }
        reverse(f+1,f+n+1);
        CDQ(1,n);sort(f+1,f+n+1);
        for(long long i=1,s=0;i<=n;++i){
            if(!f[i].T)continue;
            ans[f[i].T]-=s;s+=f[i].len;
        }
        for(int i=1;i<=m;++i)
            printf("%lld
    ",ans[i]);
        return 0;
    }
    

      

      然后看见我在上古时期的分块??!!

      一脸懵逼。

      瞪了好久发现 不是我写的。

      大概思想就是分块,每块内排序。

      然后查询:

      不在自己块里的,二分一下。

      在自己块里的,暴力搞搞。

      然后删掉。

      6000ms...过去了...

      昂波利污波。

    #include <cstdio>  
    #include <cmath>  
    #include <cstdlib>  
    #include <algorithm>  
    #include <cstring>  
    using namespace std;  
    #define ll long long  
    const int N=200050;  
    int n,m,a[N];  
    int tmp[N],t[N];//merge-sort  
    int belong[N],L[N],R[N],cnt,k,x,y;  
    int ad[N];      //i在原数列中的位置为ad[i]    
    int b[N];       //每个块维护有序数列   
    int sum[N];     //当前块中删除了多少个数   
    int adx;  
    int i;  
    bool f[N];  
    ll tot;        //当前逆序对数               开 long long !   
    inline int get(){  
      int p=0;char x=getchar();  
      while (x<'0' || x>'9') x=getchar();  
      while (x>='0' && x<='9') p=p*10+x-'0',x=getchar();  
      return p;  
    }  
    inline void merge_sort(int l,int r){  
      int mid,p,i,j;  
      mid=(l+r)>>1;  
      i=p=l;j=mid+1;  
      while (i<=mid && j<=r)  
        if (tmp[i]>tmp[j]) t[p++]=tmp[j++],tot+=mid-i+1;  
        else t[p++]=tmp[i++];  
      while (i<=mid) t[p++]=tmp[i++];  
      while (j<=r) t[p++]=tmp[j++];  
      for (i=l;i<=r;i++) tmp[i]=t[i];  
      return ;  
    }  
    inline void merge(int l,int r){  
      if (l>=r) return ;  
      int mid=(l+r)>>1;  
      merge(l,mid);  
      merge(mid+1,r);  
      merge_sort(l,r);  
      return ;  
    }  
    void init(){  
      n=get();m=get();  
      k=sqrt(n);      //块大小   
      cnt=n/k;if (n%k) cnt++; //块个数   
      for (int i=1;i<=n;i++)  
        a[i]=get(),ad[a[i]]=i,belong[i]=(i-1)/k+1;  
      for (int i=1;i<=cnt;i++)  
        L[i]=i*k-k+1,R[i]=i*k;  
      R[cnt]=n;  
      memcpy(tmp,a,sizeof tmp); 
      tot=0;  
      merge(1,n);  
      memcpy(b,a,sizeof a);  
      for (int i=1;i<=cnt;i++)  
        sort(b+L[i],b+R[i]+1);  
      memset(f,1,sizeof f);  
      return ;  
    }  
    inline int search(int t,int p){  
      int l,r,ret;  
      l=L[t]; r=R[t]; ret=R[t];  
      while (l<=r){  
        int mid=(l+r)>>1;  
        if (b[mid]<=p) ret=mid,l=mid+1;  
        else r=mid-1;  
      }  
      if (b[ret]>p) ret=L[i]-1;  
      return ret;  
    }  
    int main(){
      init();  
      for (int p=1;p<=m;p++){  
        printf("%lld
    ",tot);  
        y=get();x=ad[y];        //得到在a数列中的位置 所处的块的编号肯定不变  
        k=belong[x];            //x属于第k个块     
        for (i=1;i<k;i++)  
          tot-=R[i]-search(i,a[x]);  
        for (i=k+1;i<=cnt;i++)  
          tot-=search(i,a[x])-L[i]+1-sum[i];  
        for (i=L[k];i<x;i++)  
          if (f[a[i]] && a[i]>a[x]) tot--;  
        for (i=x+1;i<=R[k];i++)  
          if (f[a[i]] && a[i]<a[x]) tot--;  
        adx=search(k,a[x]);  
        b[adx]=0;sum[k]++;  
        f[a[x]]=0;  
        sort(b+L[k],b+R[k]+1);  
      }  
      return 0;  
    }
    

      

  • 相关阅读:
    python正则表达式(+ {})(二)
    14丨 HTTP有哪些优点?又有哪些缺点?
    python正则表达式(. *)(一)
    12丨响应状态码该怎么用?
    Fiddler—Fiddler+willow插件应用(十四)
    11丨你能写出正确的网址吗?
    【洛谷P1858】多人背包
    【洛谷P3387】(模板)缩点
    【洛谷P2184】贪婪大陆
    Leetcode: 39. Combination Sum
  • 原文地址:https://www.cnblogs.com/fenghaoran/p/7266176.html
Copyright © 2020-2023  润新知