1. 程式人生 > >洛谷 P3157 [CQOI2011]動態逆序對 解題報告

洛谷 P3157 [CQOI2011]動態逆序對 解題報告

P3157 [CQOI2011]動態逆序對

題目描述

對於序列\(A\),它的逆序對數定義為滿足\(i<j\),且\(A_i>A_j\)的數對\((i,j)\)的個數。給\(1\)\(n\)的一個排列,按照某種順序依次刪除\(m\)個元素,你的任務是在每次刪除一個元素之前統計整個序列的逆序對數。

輸入輸出格式

輸入格式:

輸入第一行包含兩個整數\(n\)\(m\),即初始元素的個數和刪除的元素個數。以下\(n\)行每行包含一個\(1\)\(n\)之間的正整數,即初始排列。以下\(m\)行每行一個正整數,依次為每次刪除的元素。

輸出格式:

輸出包含\(m\)行,依次為刪除每個元素之前,逆序對的個數。

說明

\(N\le 100000,M\le 50000\)


萬年以前樹套樹怎麼都是60pts,今天終於決定進行CDQ分治水過去。

每個元素安排三個屬性為\(P_i,A_i,D_i\)分別代表在原序列的位置,元素值和被刪時間。

然後我們統計一下\(P_i < P_j,A_i>A_j,D_i<D_j\)的個數。

然後我調了半個多小時...

終於弄明白\(P_i > P_j,A_i<A_j,D_i<D_j\)也要統計...


Code:

#include <cstdio>
#include <algorithm>
#define ll long long
const int N=1e5+10;
struct node{int a,b,p;}sq[N];
int n,m,s[N];
ll ans[N];
void add(int x,int d){while(x<=m)s[x]+=d,x+=x&-x;}
int ask(int x){int sum=0;while(x)sum+=s[x],x-=x&-x;return sum;}
bool cmp1(node n1,node n2){return n1.a>n2.a;}
bool cmp2(node n1,node n2){return n1.a<n2.a;}
void CDQ(int l,int r)
{
    if(l==r) return;
    int mid=l+r>>1;
    CDQ(l,mid),CDQ(mid+1,r);
    std::sort(sq+l,sq+r+1,cmp1);
    for(int i=l;i<=r;i++)
    {
        if(sq[i].p<=mid) add(sq[i].b,1);
        else ans[sq[i].b]+=1ll*(ask(m)-ask(sq[i].b-1));
    }
    for(int i=l;i<=r;i++) if(sq[i].p<=mid) add(sq[i].b,-1);
    std::sort(sq+l,sq+r+1,cmp2);
    for(int i=l;i<=r;i++)
    {
        if(sq[i].p>mid) add(sq[i].b,1);
        else ans[sq[i].b]+=1ll*(ask(m)-ask(sq[i].b-1));
    }
    for(int i=l;i<=r;i++) if(sq[i].p>mid) add(sq[i].b,-1);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int a,i=1;i<=n;i++)
    {
        scanf("%d",&a);
        sq[a].a=i;
        sq[a].b=m;
        sq[a].p=a;
    }
    for(int a,i=1;i<=m;i++)
    {
        scanf("%d",&a);
        sq[a].b=i;
    }
    CDQ(1,n);
    ans[m]>>=1;
    for(int i=m-1;i;i--) ans[i]+=ans[i+1];
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}

2018.11.27