BZOJ2038 小z的袜子(分块版莫队)

来源:互联网 发布:苏美尔文明 知乎 编辑:程序博客网 时间:2024/06/11 23:04

题目链接:BZOJ2038
第一次看莫队算法,写写感受。
看的第一篇博客是用平面哈夫曼距离最小生成树写的,看懂了原理,但不会写代码。后来看到了一个更简单的替代品分块,时间复杂度相近,为 n*sqrt(n) 。原理不难理解,基本思路就是通过改变处理询问顺序,降低复杂度。分块版的是按询问的左端点所在块的编号为第一关键字,右端点为第二关键字,排序之后直接暴力就好了。

复杂度分析(每次修改复杂度为 O(1)):

  • i与i+1在同一块内,r单调递增,所以r是 O(n) 的。由于有 sqrt(n) 块,所以这一部分时间复杂度是 n*sqrt(n)
  • i与i+1跨越一块,r最多变化n,由于 sqrt(n) 块,所以这一部分时间复杂度是 n*sqrt(n)
  • i与i+1在同一块内时变化不超过 sqrt(n),跨越一块也不会超过 sqrt(n),忽略系数2。由于有n个数,所以时间复杂度是 n*sqrt(n),
    于是就是 O(n*sqrt(n)) 了

然后就是模板题代码(有参考hzwer大神)

#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>using namespace std;const int maxn=50500;typedef long long LL;LL gcd(LL a,LL b){    return (b==0)?a:gcd(b,a%b);}int pos[maxn],col[maxn],f[maxn],n,m;struct Query{    int l,r,id;    LL a,b;    friend bool operator < (const Query &R,const Query &T)    {        return (pos[R.l]<pos[T.l])||(pos[R.l]==pos[T].l&&R.r<T.r);    }    void modify()    {        LL k=gcd(a,b);        a/=k,b/=k;    }}Q[maxn];bool cmp_id(const Query &a,const Query &b){    return a.id<b.id;}void init(){    scanf("%d%d",&n,&m);    for (int i=1;i<=n;i++)     scanf("%d",&col[i]);    int limit=(int)sqrt((double)n+0.5);    for (int i=1;i<=n;i++)     pos[i]=(i-1)/limit+1;    for (int i=1;i<=m;i++)    {        scanf("%d%d",&Q[i].l,&Q[i].r);        Q[i].id=i;    }    sort(Q+1,Q+m+1);}void modify(int p,LL &ans,int add){    ans=ans+2*add*f[col[p]]+1;    f[col[p]]+=add;}void solve(){    LL ans=0;    int l=1,r=0;    for (int i=1;i<=m;i++)    {        if (r<Q[i].r)        {            for (r=r+1;r<Q[i].r;r++)             modify(r,ans,1);            modify(r,ans,1);        }        if (Q[i].l<l)        {            for (l=l-1;l>Q[i].l;l--)             modify(l,ans,1);            modify(l,ans,1);        }        if (Q[i].r<r)        {            for (;Q[i].r<r;r--)             modify(r,ans,-1);        }        if (Q[i].l>l)        {            for (;Q[i].l>l;l++)             modify(l,ans,-1);        }        if (Q[i].l==Q[i].r)        {            Q[i].a=0,Q[i].b=1;            continue;        }        Q[i].a=ans-(Q[i].r-Q[i].l+1);        Q[i].b=(LL)(Q[i].r-Q[i].l+1)*(Q[r].r-Q[i].l);        Q[i].modify();    }    sort(Q+1,Q+m+1,cmp_id);    for (int i=1;i<=m;i++)     printf("%lld/%lld\n",Q[i].a,Q[i].b);}int main(){    init();    solve();    return 0;}
原创粉丝点击