bzoj3289(莫队+树状数组求逆序对)
2016-09-03 15:50
211 查看
给出一个序列,每次询问一个区间,将这个区间变为升序的最小操作次数,每次可以交换相邻的两个数。
每个询问就是典型的,求逆序对,通过分析发现,其实是可以转移的。当然这里只能用树状数组来转移。归并排序只能做整体的逆序对计算,通过这题,树状数组的优势就出来了。
左边删除添加一个数,就是在树状数组中找小于他的数。
右边删除添加一个数,就是在树状数组中找大于他的数。ans表示当前区间的逆序对。由此转移
总结
一:这道题需要求逆序对,而数据范围中并没有给出资料的大小,实际上可能数值非常的大,这里需要注意要离散化!
否则会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),实际上就是要找一个数据结构支持
插入 删除 和 维护答案
每个询问就是典型的,求逆序对,通过分析发现,其实是可以转移的。当然这里只能用树状数组来转移。归并排序只能做整体的逆序对计算,通过这题,树状数组的优势就出来了。
左边删除添加一个数,就是在树状数组中找小于他的数。
右边删除添加一个数,就是在树状数组中找大于他的数。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),实际上就是要找一个数据结构支持
插入 删除 和 维护答案
相关文章推荐
- 【BZOJ】3289: Mato的文件管理【区间逆序对,莫队套树状数组】
- Bzoj 3289: Mato的文件管理 莫队,树状数组,逆序对,离散化,分块
- 【BZOJ3289】【莫队分块+树状数组求逆序对】Mato的文件管理
- bzoj3289 树状数组加莫队注意这题每天的交换该是不影响的。。。还有一次交换等价于去掉一个逆序
- BZOJ 3289: Mato的文件管理[莫队算法 树状数组]
- [bzoj3289]Mato的文件管理_莫队_树状数组
- 【BZOJ3289】Mato的文件管理 莫队算法+树状数组
- bzoj3289 Mato的文件管理 莫队算法 树状数组
- 莫队+树状数组 题解【bzoj3289】Mato的文件管理
- BZOJ 3289 Mato的文件管理 莫队算法+树状数组
- 【序列莫队+树状数组】BZOJ3289-Mato的文件管理
- [bzoj3289][树状数组][莫队算法]Mato的文件管理
- BZOJ3295 动态逆序对 树套树, 树状数组套线段树(主席树)
- BZOJ 3289: Mato的文件管理|分块|树状数组
- bzoj3295 动态逆序对【树状数组套权值线段树】
- 【主席树|莫队|离线树状数组】BZOJ1878 [SDOI 2009]HH的项链
- bzoj 2789: [Poi2012]Letters 树状数组求逆序对
- 【BZOJ3295】【块状链表+树状数组】动态逆序对
- bzoj 3289 莫队 逆序对
- [BZOJ] 2141 - Atlantis - 排队 - 树状数组求逆序对 - 分块求区间比 k 小