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

题目链接: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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章