[bzoj] 3295 动态逆序对 || CDQ分治

原题

给1到n的一个排列,按照某种顺序依次删除m个元素,求每删除一个元素之前统计整个序列的逆序对数。


CDQ板题。因为删除不好处理,所以将其反过来,变为每次添加。每个数都赋予一个添加时间,每次”删除“操作后都是一次询问操作。
三维分别为时间,位置,大小。
某个元素加入后产生的贡献为时间更小、位置更小、数更大的个数 + 时间更小、位置更大、数更小的个数。

#include<cstdio>
#include<algorithm>
#define N 1000010
typedef long long ll;
using namespace std;
struct hhh
{
    ll pos,w,tm;
    bool operator < (const hhh &b) const
	{
	    return tm<b.tm;
	}
}p[N];
ll n,m,ans[N],f[N],x;

bool cmp(hhh a,hhh b)
{
    return a.w>b.w;
}

ll read()
{
    ll ans=0,fu=1;
    char j=getchar();
    for (;j<'0' || j>'9';j=getchar()) if (j=='-') fu=-1;
    for (;j>='0' && j<='9';j=getchar()) ans*=10,ans+=j-'0';
    return ans*fu;
}

void add(int x,int y)
{
    while (x<=n) f[x]+=y,x+=x&-x;
}

ll query(int x)
{
    int ans=0;
    while (x) ans+=f[x],x-=x&-x;
    return ans;
}

void solve(int l,int r)
{
    if (l==r) return ;
    int mid=(l+r)>>1;
    solve(l,mid);
    solve(mid+1,r);
    sort(p+l,p+mid+1,cmp);
    sort(p+mid+1,p+r+1,cmp);
    int ll=l,rr=mid+1;
    while (rr<=r)
    {
	while (ll<=mid && p[ll].w>p[rr].w)
	    add(p[ll].pos,1),ll++;
	ans[p[rr].tm]+=query(p[rr].pos);
	rr++;
    }
    while (--ll>=l) add(p[ll].pos,-1);//clear
    ll=mid;
    rr=r;
    while (rr>mid)
    {
	while (ll>=l && p[ll].w<p[rr].w)
	    add(p[ll].pos,1),ll--;
	ans[p[rr].tm]+=query(n)-query(p[rr].pos);
	rr--;
    }
    while (++ll<=mid) add(p[ll].pos,-1);
}

int main()
{
    n=read();
    m=read();
    for (int i=1;i<=n;i++)
    {
	x=read();
	p[x].w=x;
	p[x].pos=i;
    }
    for (int i=1;i<=m;i++)
    {
	x=read();
	p[x].tm=n-i+1;//插入时间,倒叙按插入处理
    }
    for (int i=1,cnt=1;i<=n;i++)
	if (!p[i].tm) p[i].tm=cnt++;
    sort(p+1,p+n+1);
    solve(1,n);
    for (int i=2;i<=n;i++)
	ans[i]+=ans[i-1];
    for (int i=n;i>n-m;i--)
	printf("%lld\n",ans[i]);
    return 0;
}
posted @ 2017-12-18 15:12  Mrha  阅读(145)  评论(0编辑  收藏  举报