BZOJ2038: [2009国家集训队]小Z的袜子(hose) 莫队算法

要使用莫队算法前提 ,已知[l,r]的答案,要能在logn或者O(1)的时间得到[l+1,r],[l-1,r],[l,r-1],[l,r+1],适用于一类不修改的查询

优美的替代品——分块
将n个数分成sqrt(n)块
按区间排序,以左端点所在块内为第一关键字,右端点为第二关键字,进行排序
也就是以( pos [l],r )排序
然后搞就可以了
 

搞得过程是这样的:
一、i与i+1在同一块内,r单调递增,所以r是O(n)的。由于有n^0.5块,所以这一部分时间复杂度是n^1.5。
二、i与i+1跨越一块,r最多变化n,由于有n^0.5块,所以这一部分时间复杂度是n^1.5
三、i与i+1在同一块内时变化不超过n^0.5,跨越一块也不会超过2*n^0.5,不妨看作是n^0.5。由于有n个数,所以时间复杂度是n^1.5
于是就变成了O(n^1.5)了

——————————以上来自http://hzwer.com/2782.html,略有修改

黄学长的前两条说的是更新区间右端点r的复杂度是O(n^1.5),这个说的很清楚;

最后一条说的的是更新区间左端点l的复杂度,我想略做添加

排序后的i和i+1个查询如果在同一块变化不超过sqrt(n),但是当跨越一块时,最大却不止是2*n^0.5,可以是n,

这样我们可以在每一个没有查询的块里添加一个辅助查询区间(l=该块的右界,r为下一个真正询问的右界 ),这样最多添加n^0.5这样的查询点 这样总的 查询个数是 n+n^0.5;

这样再应用黄学长第三条,i,i+1在一块,l不超过n^0.5,跨越一块(此时由于添加辅助查询点)最多是2*n^0.5

一共有n+n^0.5个查询 所以复杂度是 (n+n^0.5)*n^0.5=(n^0.5+1)n 所以复杂度是n^1.5

(添加辅助点不会变慢)

所以最后复杂度是O(n^1.5) 

 

代码如下:(代码写的不好,如果要看还是看上述链接的比较好,以下仅供娱乐)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<iostream>
#include<cstdlib>
#include<queue>
#include<map>
#include<set>
#include<cmath>
using namespace std;
typedef long long LL;
const int maxn=50001;
LL gcd(LL a, LL b)
{
    if(a==0)return b;
    while(a%b)
    {
        LL t=a%b;
        a=b;
        b=t;
    }
    return b;
}
int num[maxn],pos[maxn],o[maxn];
LL ans=0;
struct node
{
    int l,r,id;
    LL a,b;
} res[maxn];
bool cmp1(node x,node y)
{
    if(pos[x.l]==pos[y.l])return x.r<y.r;
    return x.l<y.l;
}
bool cmp2(node x,node y)
{
    return x.id<y.id;
}
void change(int pos,int op)
{
    LL t=num[o[pos]];
    ans-=t*(t-1)/2;
    num[o[pos]]+=op;
    t=num[o[pos]];
    ans+=t*(t-1)/2;
}
int main()
{
    int n,m;
    ans=0;
    scanf("%d%d",&n,&m);
    int block=(int)(sqrt(n));
    for(int i=1; i<=n; ++i)
        scanf("%d",&o[i]),pos[i]=(i-1)/block+1;
    for(int i=1; i<=m; ++i)
        scanf("%d%d",&res[i].l,&res[i].r),res[i].id=i;
    sort(res+1,res+m+1,cmp1);
    for(int i=1,l,r; i<=m; ++i)
    {
        if(i==1)
            for(int j=res[i].l; j<=res[i].r; ++j)
                change(j,1);
        else
        {
            for(; r+1<=res[i].r; ++r)
                change(r+1,1);
            for(; r>res[i].r; --r)
                change(r,-1);
            for(; l<res[i].l; ++l)
                change(l,-1);
            for(; l-1>=res[i].l; --l)
                change(l-1,1);
        }
        l=res[i].l,r=res[i].r;
        if(res[i].l==res[i].r)
        {
            res[i].a=0;
            res[i].b=1;
            continue;
        }
        res[i].a=ans;
        LL t=res[i].r-res[i].l+1;
        res[i].b=t*(t-1)/2;
        t=gcd(res[i].a,res[i].b);
        res[i].a/=t;
        res[i].b/=t;
    }
    sort(res+1,res+1+m,cmp2);
    for(int i=1; i<=m; ++i)
        printf("%lld/%lld\n",res[i].a,res[i].b);
    return 0;
}
View Code

 

posted @ 2015-12-10 11:04  shuguangzw  阅读(163)  评论(0编辑  收藏  举报