CDQ二层试炼:
bzoj3295
http://www.lydsy.com/JudgeOnline/problem.php?id=3295
题意大致是:
给你一个序列(一个1~n的排列),每次删掉一个数,求删掉之前的逆序对。
方案1:强行模拟,每次删掉一个一个数,在剩下的数里面用归并求逆序对,复杂度O(mnlogn)
这个复杂度明显是不能接受的
方案2:树套树,代码量令人伤心。
方案3:cdq分治。
之前已经写过一篇浅谈CDQ的了,那到底什么是CDQ呢?
我认为CDQ是一种思想,一种分治的思想。在三维偏序中CDQ的思想就是一维排序,二维分治,三维树状数组。
在这道题里面,我们可以把这道题看成是求(x,y,z)这个点被删除后对总答案的影响。
我们把 当前位置i作为x,把i位置的数的大小作为y ,把删除时间作为z(初始为n||无穷大,就是对答案无影响)
接下来考虑 删去(x,y,z)对答案有什么影响。 首先一点最容易想到,它只会影响到zi>z的点(之前的点都已经被删去,那些点不会影响到逆序对的个数).
于是,我们就可以对z这一维排序。(我是从大到小排的,这样处理比较方便)
这样,删去一个点只会对在它左边的点的某些点构成逆序对的减少。
在zi>z的情况下接着考虑删去(x,y,z)的影响:
如果xi>x&&yi
如果xi
接下来怎么做?
z这一维已经排好序了,我们仍然进行分治。
(下面以分治x,数据结构维护y为例。 PS:我的代码是分治y,数据结构维护x,大家可以推一下,熟悉一下)
考虑[l,mid],[mid+1,r]的2分区间, 分别将按x进行两次排序.
然后考虑左区间对右区间的影响:
(这里是求删去当前点对答案的影响)
①按x递减排序, 枚举右区间j,在左区间一直更新 i.x>j.x的i,将i.y在树状数组中的值+1(逆序对数+1),然后查询[1,j.y)这个区间的和,把它累加到当前点的影响中。
按x递增排序, 枚举右区间的j,在左区间一直更新 i.x
然后就做完了.
分治的时候先调用左区间,再调用右区间,然后计算左区间对右区间的影响。(其实先后顺序可以交换,习惯而已)
然后建议自己推一下分治y,维护x的情况(我的代码也是这种)
然后写这一篇文章的目的是加深对CDQ的理解,but我光荣的调了好久的bug,从WA成T,最后改了一下分治的sort,优化了常数才过的!
附上AC代码:
#include
#include
#include
#include
#include
#include
#include
#define LL long long
using namespace std;
const LL maxn=100000+20;
LL n,m;
struct node
{
LL x,y,z;//数的位置x,大小y,删除时间z
}q[maxn],r1[maxn],r2[maxn];
LL shan[maxn];
bool cmpy(node a,node b)
{
return a.yb.z;
if(a.y!=b.y)return a.y>b.y;
return a.xb.y;
}
bool cmpx(node a,node b)
{
return a.x>b.x;
}
long long fl[maxn],fr[maxn];
/*
恒保证 左区间z >右区间 删除右区间某个值, 对答案的贡献是
对于一个点(x,y,z)删掉它减少的逆序对是左区间 zi>z :
xi>x yiy 记为fl
*/
long long f[maxn];
void updata(LL i)
{
while(i<=n)
{
f[i]++;
i+=(i&-i);
}
}
void clear(LL i)
{
while(i<=n)
{
f[i]=0;
i+=(i&-i);
}
}
LL query(LL i)
{
LL ans=0;
while(i)
{
ans+=f[i];
i-=(i&-i);
}
return ans;
}
void work(LL l,LL r)
{
LL mid=(l+r)>>1;
int rt1,rt2;
rt1=rt2=0;
for(int i=l;i<=mid;i++)r1[++rt1]=q[i];
for(int i=mid+1;i<=r;i++)r2[++rt2]=q[i];
sort(r1+1,r1+rt1+1,cmpy2);
sort(r2+1,r2+rt2+1,cmpy2);
//计算所有yi>yj 的xir2[j].y;i++)updata(r1[i].x);
fl[r2[j].y]+=query(r2[j].x);
}
for(LL i=1;i<=rt1;i++)clear(r1[i].x);
//计算所有xi>x yi=1;j--)
{
for(;i>=1&&r1[i].yq[j].x;i++)updata(q[i].y);
fr[q[j].y]+=query(q[j].y);
}
for(LL i=l;i<=mid;i++)clear(q[i].y);
sort(q+l,q+r+1,cmpz);
*/
}
void cal(LL l,LL r)
{
if(l==r)return ;
LL mid=(l+r)>>1;
cal(l,mid);
cal(mid+1,r);
work(l,r);
}
long long sum=0;
long long t[maxn];
long long a[maxn],b[maxn];
long long cao[maxn];
void guibing(LL l,LL r)
{
if(l==r)
{
cao[l]=t[l];
return ;
}
LL mid=(l+r)>>1;
LL t1,t2;
t1=t2=0;
guibing(l,mid);
for(LL i=l;i<=mid;i++)a[i]=cao[i];
guibing(mid+1,r);
for(LL i=mid+1;i<=r;i++)b[i]=cao[i];
LL i=l,j=mid+1;
LL tt=l;
while(i<=mid&&j<=r)
{
if(a[i]>b[j])
{
sum+=mid-i+1;
cao[tt++]=b[j++];
}
else cao[tt++]=a[i++];
}
while(i<=mid)cao[tt++]=a[i++];
while(j<=r)cao[tt++]=b[j++];
}
int main()
{
scanf("%lld%lld",&n,&m);
for(LL i=1;i<=n;i++)
{
q[i].x=i;
scanf("%lld",&q[i].y);
q[i].z=n;
t[i]=q[i].y;
}
sort(q+1,q+n+1,cmpy);
for(LL i=1;i<=m;i++)
{
LL x;
scanf("%lld",&x);
q[x].z=i;
shan[i]=x;
}
sort(q+1,q+n+1,cmpz);
cal(1,n);
//
guibing(1,n);
for(LL i=1;i<=m;i++)
{
printf("%lld\n",sum);
sum-=fl[shan[i]]+fr[shan[i]];
}
return 0;
}