莫队+分块 【2009国家集训队】小Z的袜子(hose) bzoj2038

来源:互联网 发布:淘宝企业店铺怎么注册 编辑:程序博客网 时间:2024/06/11 03:33

莫队算法,听起来就很高端的样子啊,但其实还挺好理解的。
就是如果你查询的东西满足你已知[l,r]区间的答案,就能在很快的时间内知道[l+1,r],[l-1,r],[l,r+1],[l,r-1]这四个区间的答案,就可以用莫队搞。

按照l第一关键字,r第二关键字排序,把所有的询问扫一遍,l是递增的,然后把r每次调到它该在的位置,统计答案,这样做时间复杂度上界是n^2的,有点接受无能啊……

所以就可以用我怎么想也想不到的但是神犇们就是能想到的分块了。

把区间分块,然后l在一个块内的询问按照r排序,然后每次处理一个块内的所有询问。
这样可以保证没次询问l的修改范围不超过sqrt(n),总共有m个询问,每个块内对于r的所有修改不超过n,一共有sqrt(n)块。
再加上排序的时间mlogm。

所以总的时间复杂度就是O(msqrt(n)+nsqrt(n)+mlogm)
简化一下就是O(nsqrt(n))啦!
over

代码参照了PoPo姐姐写的>_<,挺好懂的。

代码如下:

#include<cstdio>#include<algorithm>#include<cmath>#define N 50050using namespace std;struct query{    int l,r,num;    bool operator < (const query&) const;}q[N];int n,m,block;int a[N],belong[N];long long num[N],den[N],cnt[N];bool query ::  operator < (const query &c) const{    return belong[l]<belong[c.l] || (belong[l]==belong[c.l] && r<c.r);}long long gcd(long long a,long long b){    if(!b) return a;    return gcd(b,a%b);}int main(){    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++) scanf("%d",&a[i]);    for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].num=i;    block=ceil(sqrt(n));    for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;    sort(q+1,q+1+m);    long long now=0;    long long l=1,r=0;    for(int i=1;i<=m;i++)    {        while(r<q[i].r) now+=cnt[a[++r]]++;        while(r>q[i].r) now-=--cnt[a[r--]];        while(l<q[i].l) now-=--cnt[a[l++]];        while(l>q[i].l) now+=cnt[a[--l]]++;        long long sum=(r-l)*(r-l+1)>>1;        long long div=gcd(sum,now);        num[q[i].num]=now/div;        den[q[i].num]=sum/div;    }    for(int i=1;i<=m;i++) printf("%lld/%lld\n",num[i],den[i]);    return 0;}
0 0
原创粉丝点击