您的位置:首页 > 其它

bzoj3289(莫队+树状数组求逆序对)

2016-09-03 15:50 211 查看
给出一个序列,每次询问一个区间,将这个区间变为升序的最小操作次数,每次可以交换相邻的两个数。

 

每个询问就是典型的,求逆序对,通过分析发现,其实是可以转移的。当然这里只能用树状数组来转移。归并排序只能做整体的逆序对计算,通过这题,树状数组的优势就出来了。

左边删除添加一个数,就是在树状数组中找小于他的数。

右边删除添加一个数,就是在树状数组中找大于他的数。ans表示当前区间的逆序对。由此转移

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
typedef long long ll;
inline int read()
{
int ans,f=1;char ch;
while ((ch=getchar())<'0'||ch>'9') if (ch=='-') f=-1;
ans=ch-'0';
while ((ch=getchar())>='0'&&ch<='9') ans=ans*10+ch-'0';
return ans*f;
}
const int N=100005;

int n,m,pos
;
int s
,ss
;
ll ml,mr,ans;

/////////////////////////////////////
int lowbit(int x)
{
return x&(-x);
}
int t
;
void add(int i,int x)
{
while (i<=n)
{
t[i]+=x;
i+=lowbit(i);
}
}
int query(int i)
{
int ans=0;
while (i>0)
{
ans+=t[i];
i-=lowbit(i);
}
return ans;
}

////////////////////////////////////
struct aa
{
int l,r,id;
ll ans;
}q
;
bool cmp1(aa a,aa b)
{
if (pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
return a.r<b.r;
}
bool cmp2(aa a,aa b)
{
return a.id<b.id;
}

void solve()
{
ml=1;mr=0;
for (int i=1;i<=m;i++)
{
for (;mr>q[i].r;mr--)
{
int num=query(s[mr]-1);
int sum=mr-ml;
ans-=sum-num;
add(s[mr],-1);
}
for (;mr<q[i].r;mr++)
{
int num=query(s[mr+1]-1);
int sum=mr-ml+1;
ans+=sum-num;
add(s[mr+1],1);
}
/////////////////////////////
for (;ml<q[i].l;ml++)
{
int num=query(s[ml]-1);
ans-=num;
add(s[ml],-1);
}
for (;ml>q[i].l;ml--)
{
int num=query(s[ml-1]-1);
ans+=num;
add(s[ml-1],1);
}

q[i].ans=ans;
}
}

int main()
{
n=read();
int block=int(sqrt(n));
for (int i=1;i<=n;i++) pos[i]=(i-1)/block+1;

for (int i=1;i<=n;i++) s[i]=ss[i]=read();
sort(ss+1,ss+n+1);
int nn=unique(ss+1,ss+n+1)-ss-1;
for (int i=1;i<=n;i++) s[i]=lower_bound(ss+1,ss+nn+1,s[i])-ss;

m=read();
for (int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;

sort(q+1,q+m+1,cmp1);
solve();
sort(q+1,q+m+1,cmp2);

for (int i=1;i<=m;i++) printf("%lld\n",q[i].ans);
return 0;
}

总结

一:这道题需要求逆序对,而数据范围中并没有给出资料的大小,实际上可能数值非常的大,这里需要注意要离散化!

否则会RE,同时整理RE的几种情况

1:除0

2:数组开小(这里是题目性质没有分析好,没有进行离散化)

3:大数组于函数中(包括main()),导致的爆栈。

二:莫队的算法,其实是很灵活的,有的时候不仅仅是简简单单的添加和删除。这里,在区间的左边右边添加删除一个数,左右两边的操作是不一样的,但是只要保证了,莫队问题的那些基本的要求就可以。

总结的基本要求

1:离线,并且问l~r区间的ans,要保证询问之间互不影响

2:我们可以编写add和remove函数(实际上就是已知l~r的答案,可以快速得到l+1~r,l~r-1的答案)这里转移一般是O(1)或者log(n),实际上就是要找一个数据结构支持

插入   删除   和  维护答案

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: