您的位置:首页 > 其它

[CQOI2011] 动态逆序对之CDQ解法+洛谷1393CDQ解法

2018-01-15 16:41 519 查看

前言

第一次做这道题目,用的是主席树套树状数组的方法,思想非常清晰明了,就是用数据结构进行暴力维护统计,然鹅主席树确实不好打,再套个树状数组,Debug就更困难了。

主席树套树状数组讲解链接

今天学习了基本的CDQ思想及方法,忽然记起这道题目有CDQ的解法,于是做了做。

不得不说 CDQ真是又好码,跑的又快,然鹅推到过程却不像上一种解法那么好搞。

思路分析

要转化成CDQ的模型,首先得有时间序,我们把删除的顺序看做时间序,然后把删除看做倒着插入,越早插入的时间序越小。

然后把每个位置上的数看做一维,把位置看做一维。

样例变成了

t 1 2 3 4 5(时间序)

x 3 5 4 1 2(大小序)

y 3 2 4 1 5(位置序)

我们设一个数为(t0,x0,y0)

我们求解时,贡献是在它插入之前右边多少比它大的数,左边多少比它数

即 1.t<t0,x<x0,y>=y02.t<t0,x>x0,y<=y0

然后为了把不等号搞的方向一致,转化为

1.t<t0,x<x0,y<=(n−y0+1)2.t<t0,x<(n−x0+1),y<=y0

这样就转化为三维偏序问题了,直接上CDQ统计答案即可。

注意事项

由于求的是逆序对总和,很明显要开long long,可不要忘了。

输出上一次删除的答案,就是最后一个删除不用输出啦。

题目链接

BZOJ

COGS

Luogu

Ac Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#define il inline
#define lowbit(x) x&(-x)
#define ll long long
using namespace std;
const int maxm=111000;
struct node{
int x,y,z,id;
ll ans;
};
node a[maxm],b[maxm];
int m,n,val[maxm],rk[maxm];
ll ans[maxm],sum[maxm];
il int read()
{
int x=0,w=1;
char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*w;
}
il void ins(int x,int w){for(int i=x;i<=maxm;i+=lowbit(i)) sum[i]+=(ll)w;}
il ll ask(int x){ll ans=0;for(int i=x;i;i-=lowbit(i)) ans+=sum[i];return ans;}
il bool same(node x1,node x2){return (x1.x==x2.x)&&(x1.y==x2.y)&&(x1.z==x2.z);}
il bool comp(node x1,node x2){return (x1.x<x2.x)||(x1.x==x2.x&&(x1.y<x2.y||(x1.y==x2.y&&x1.z<x2.z)));}
void CDQ(int l,int r)
{
if(l>=r) return;
int mid=(l+r)>>1;
CDQ(l,mid),CDQ(mid+1,r);
for(int i=l,j=l,p=mid+1;i<=r;i++)
{
if(j<=mid&&(p>r||a[j].y<=a[p].y)) b[i]=a[j++];
else b[i]=a[p++];
}//归并排序,在这段区间中把y排好.
for(int i=l;i<=r;i++)
{
a[i]=b[i];
if(a[i].id<=mid) ins(a[i].z,1);//插入小的
else a[i].ans+=ask(a[i].z);//大的查询
}
for(int i=l;i<=r;i++)
if(a[i].id<=mid) ins(a[i].z,-1);//消除影响
}
int main()
{
//freopen("inverse.in","r",stdin);
//freopen("inverse.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++) a[i].y=i,a[i].z=read();
for(int i=1,w;i<=m;i++) w=read(),rk[w]=i;
int tot=m;
for(int i=1;i<=n;i++) if(!rk[i]) rk[i]=++tot;
for(int i=1;i<=n;i++) a[i].x=tot-rk[a[i].z]+1;
sort(a+1,a+n+1,comp);
for(int i=1;i<=n;i++) a[i].y=n-a[i].y+1,a[i].id=i;
CDQ(1,n);
for(int i=1;i<=n;i++) ans[a[i].x]+=a[i].ans;
sort(a+1,a+n+1,comp);
for(int i=1;i<=n;i++) a[i].y=n-a[i].y+1,a[i].z=n-a[i].z+1,a[i].ans=0,a[i].id=i;
CDQ(1,n);
for(int i=1;i<=n;i++) ans[a[i].x]+=a[i].ans;
for(int i=1;i<=n;i++) ans[i]+=ans[i-1];
for(int i=n;i>n-m;i--) printf("%lld\n",ans[i]);
return 0;
}


洛谷用户福利

有一道非常相似的题目

1393

连名字都一样。

但是这双倍经验可是不好得的。

1.这题目没有保证数据<=n(所以我们要离散化)

2.给出的是删除的数的位置,而不是数值。

3.先输出总的逆序对个数,然后输出每个删除后逆序对的个数。

Ac Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#define il inline
#define lowbit(x) x&(-x)
#define ll long long
using namespace std;
const int maxm=111000;
struct node{
int x,y,z,id;
ll ans;
};
node a[maxm],b[maxm];
int m,n,val[maxm],rk[maxm];
ll ans[maxm],sum[maxm];
il int read()
{
int x=0,w=1;
char ch=0;
while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*w;
}
il void ins(int x,int w){for(int i=x;i<=maxm;i+=lowbit(i)) sum[i]+=(ll)w;}
il ll ask(int x){ll ans=0;for(int i=x;i;i-=lowbit(i)) ans+=sum[i];return ans;}
il bool same(node x1,node x2){return (x1.x==x2.x)&&(x1.y==x2.y)&&(x1.z==x2.z);}
il bool comp(node x1,node x2){return (x1.x<x2.x)||(x1.x==x2.x&&(x1.y<x2.y||(x1.y==x2.y&&x1.z<x2.z)));}
void CDQ(int l,int r)
{
if(l>=r) return;
int mid=(l+r)>>1;
CDQ(l,mid),CDQ(mid+1,r);
for(int i=l,j=l,p=mid+1;i<=r;i++)
{
if(j<=mid&&(p>r||a[j].y<=a[p].y)) b[i]=a[j++];
else b[i]=a[p++];
}//归并排序,在这段区间中把y排好.
for(int i=l;i<=r;i++)
{
a[i]=b[i];
if(a[i].id<=mid) ins(a[i].z,1);//插入小的
else a[i].ans+=ask(a[i].z);//大的查询
}
for(int i=l;i<=r;i++)
if(a[i].id<=mid) ins(a[i].z,-1);//消除影响
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++) a[i].y=i,a[i].z=read(),val[i]=a[i].z;
sort(val+1,val+n+1);
int t=unique(val+1,val+n+1)-val-1;
for(int i=1;i<=n;i++) a[i].z=lower_bound(val+1,val+t+1,a[i].z)-val;
for(int i=1,w;i<=m;i++) w=read(),rk[w]=i;
int tot=m;
for(int i=1;i<=n;i++) if(!rk[i]) rk[i]=++tot;
for(int i=1;i<=n;i++) a[i].x=tot-rk[i]+1;
sort(a+1,a+n+1,comp);
for(int i=1;i<=n;i++) a[i].y=n-a[i].y+1,a[i].id=i;
CDQ(1,n);
for(int i=1;i<=n;i++) ans[a[i].x]+=a[i].ans;
sort(a+1,a+n+1,comp);
for(int i=1;i<=n;i++) a[i].y=n-a[i].y+1,a[i].z=n-a[i].z+1,a[i].ans=0,a[i].id=i;
CDQ(1,n);
for(int i=1;i<=n;i++) ans[a[i].x]+=a[i].ans;
for(int i=1;i<=n;i++) ans[i]+=ans[i-1];
for(int i=n;i>=n-m;i--) printf("%lld ",ans[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: