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