[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;
}