【BZOJ】2038 小Z的袜子
2016-01-23 11:56
337 查看
Problem
【题意】在长度为nn的序列aa中,有mm个询问,每次求区间[l,r][l,r]中选择两个点(ai,aj)(a_i,a_j),满足ai≠aja_i\neq a_j。【数据范围】
2≤N,M≤500002\leq N,M\leq 50000
1≤L<R≤N1\leq L
1≤ai≤N1\leq a_i\leq N
Analysis
莫队算法怎么做?百度一下随便点开即可。本身做这道题就是为了打一遍模板,然后复习一下复杂度分析,用来准备分析一下强制在线莫队的复杂度和UnitUnit的取值大小的推导。
然而模板不小心打错了,”//”写成了”<<”,不小心弄了一个上午……
下面开始分析复杂度。
先是把询问排序,我的写法:
第一关键字是ll所在的块,
第二关键字是rr的大小。
时间复杂度为O(mlogm)O(m\log m)。
然后是莫队算法。
①当ll在块内移动时,每次移动m−−√\sqrt m以内,最多移动nn次。
时间复杂度为:O(mn−√)O(m\sqrt n)
②当ll移动到块外时,总共最多能移动nn步。
时间复杂度为:O(n)O(n)
③当rr移动时,在ll在同一块内,最多移动nn步,而一共不超过n−√\sqrt n个块。
时间复杂度为:O(nn−√)O(n\sqrt n)
综上所述,总的时间复杂度为:
O(mlogm)+O(mn−√)+O(n)+O(nn−√)=O(nn−√)O(m\log m)+O(m\sqrt n)+O(n)+O(n\sqrt n)=O(n\sqrt n)。
回顾上述的分析方法,也就是按照块内外、不同的端点分类讨论而已。
Question
其实看了VFK糖果公园的题解,我还想到了一个问题:排序的时候,第一关键字为ll所在的块,第二关键字为rr所在的块行不行?
其实是可以的,这里再来一次分析当练习。
分以下44类讨论:
①当ll在块内移动时,O(mn−√)O(m\sqrt n)
②当ll移动到块外时,O(n)O(n)
③当rr在块内移动时,O(mn−√)O(m\sqrt n)
④当rr移动到块外时,O(nn−√)O(n\sqrt n)
综上所述,时间复杂度为O(nn−√)O(n\sqrt n)。
Code
【代码1】排序方式:
第一关键字:ll所在的块;
第二关键字:rr从小到大。
实测:2080 ms
#include <cstdio> #include <cctype> #include <cmath> #include <algorithm> using namespace std; typedef long long Lint; const int N=65536; const int M=65536; int n; int a ; int m; int unit; struct Ques { int l,r,id; friend inline int operator < (Ques qa,Ques qb) { return qa.l/unit!=qb.l/unit?qa.l/unit<qb.l/unit:qa.r<qb.r; } }q[M]; Lint ans[M][2]; int cnt ; int l=1,r=0; Lint res; inline int read(void) { int x=0,f=1; char c=getchar(); for (;!isdigit(c);c=getchar()) if (c=='-') f=-1; for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline void add(int w,int k) { res-=(Lint)cnt[w]*(cnt[w]-1)>>1; cnt[w]+=k; res+=(Lint)cnt[w]*(cnt[w]-1)>>1; } inline Lint gcd(Lint i,Lint j) { for (Lint r;j;r=i%j,i=j,j=r); return i; } int main(void) { n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i; unit=(int)sqrt(n); sort(q+1,q+m+1); Lint g; for (int i=1;i<=m;i++) { for (;l<q[i].l;l++) add(a[l],-1); for (;l>q[i].l;l--) add(a[l-1],1); for (;r<q[i].r;r++) add(a[r+1],1); for (;r>q[i].r;r--) add(a[r],-1); ans[q[i].id][0]=res,ans[q[i].id][1]=(Lint)(q[i].r-q[i].l+1)*(q[i].r-q[i].l)>>1; g=gcd(ans[q[i].id][0],ans[q[i].id][1]); for (int k=0;k<=1;k++) ans[q[i].id][k]/=g; } for (int i=1;i<=m;i++) printf("%lld/%lld\n",ans[i][0],ans[i][1]); return 0; }
【代码2】
排序方式:
第一关键字:ll所在的块
第二关键字:rr所在的块
实测:2396 ms
#include <cstdio> #include <cctype> #include <cmath> #include <algorithm> using namespace std; typedef long long Lint; const int N=65536; const int M=65536; int n; int a ; int m; int unit; struct Ques { int l,r,id; friend inline int operator < (Ques qa,Ques qb) { return qa.l/unit!=qb.l/unit?qa.l/unit<qb.l/unit:qa.r/unit<qb.r/unit; } }q[M]; Lint ans[M][2]; int cnt ; int l=1,r=0; Lint res; inline int read(void) { int x=0,f=1; char c=getchar(); for (;!isdigit(c);c=getchar()) if (c=='-') f=-1; for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline void add(int w,int k) { res-=(Lint)cnt[w]*(cnt[w]-1)>>1; cnt[w]+=k; res+=(Lint)cnt[w]*(cnt[w]-1)>>1; } inline Lint gcd(Lint i,Lint j) { for (Lint r;j;r=i%j,i=j,j=r); return i; } int main(void) { n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); for (int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i; unit=(int)sqrt(n); sort(q+1,q+m+1); Lint g; for (int i=1;i<=m;i++) { for (;l<q[i].l;l++) add(a[l],-1); for (;l>q[i].l;l--) add(a[l-1],1); for (;r<q[i].r;r++) add(a[r+1],1); for (;r>q[i].r;r--) add(a[r],-1); ans[q[i].id][0]=res,ans[q[i].id][1]=(Lint)(q[i].r-q[i].l+1)*(q[i].r-q[i].l)>>1; g=gcd(ans[q[i].id][0],ans[q[i].id][1]); for (int k=0;k<=1;k++) ans[q[i].id][k]/=g; } for (int i=1;i<=m;i++) printf("%lld/%lld\n",ans[i][0],ans[i][1]); return 0; }
Sumarize
首先是写莫队需要注意的一个地方:在排序的时候,不要把”//”写成了”<<”。
然后是莫队算法的排序方式:
按块来分,最后一个关键字可以直接从小到大。
近而回顾离线算法的排序方式。本来是这样的:
①按照左端点排序 ②按照右端点排序 ③按照块排序
现在可以补充一点,就是说我们可以使用多关键字排序。
然后是莫队的复杂度分析的方法:
按照不同端点、块内外来分类讨论。
就这些了,希望不要再因为类似的错误导致一个上午的颓废。
相关文章推荐
- VTK大体流程
- JSP
- 【自己总结的】HBase基本命令
- listview之adapter的优化
- XCode7,打包上传的一些警告,及参考处理方法
- List 去重(java)
- 快速掌握粒子编辑器 —— onebyonedesign网页版
- ZOJ 1049 题目大意就是路易斯安那每年缩减50 square miles,缩减的是以(0,0)为圆心的半圆,对于任意x,y坐标,输出几年之后被淹没
- OpenWRT添加物理按键(procd,ar71xx)
- 页面自动跳转的几种方式
- 利用Java进行MySql数据库的导入和导出
- 给小白的Java EE生存指南(6) :Java 反射
- 青年论坛:谈判的情感力量
- Java语法--思维导图
- SpringMVC+hibernate框架整合源码SSH Dubbo,ZooKeeper
- C++ 析构函数——转自“九天雁翎”博客
- LINQ查询操作符之First、FirstOrDefault、Last、LastOrDefault、ElementAt、ElementAtOrDefault、Contains、Any、All、Coun
- virtualbox vb 虚拟机网卡工作模式
- MyEclipse连接MySql数库
- Android闪屏效果实现方法