bzoj2038 小Z的袜子(hose)——莫队算法

Posted zinn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj2038 小Z的袜子(hose)——莫队算法相关的知识,希望对你有一定的参考价值。

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2038

就是莫队算法;

先写了个分块,惨WA:

技术分享图片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=50005;
int n,m,K,c[maxn],rk[maxn],cnt[maxn],cnt2[maxn],ct=1,t,tmp[maxn];
ll sum,s;
struct N{int l,r;ll ans,ans2;}q[maxn];
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<0||ch>9)ch=getchar();
    while(ch>=0&&ch<=9)ret=ret*10+ch-0,ch=getchar();
    return ret;
}
bool cmp(int x,int y){return q[x].l<q[y].l;}
bool cmp2(int x,int y){return q[x].r<q[y].r;}
int C(int x){return x*(x-1)/2;}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void yf(N &x)//&
{
    ll k=gcd(x.ans2,x.ans);
    x.ans/=k;x.ans2/=k;
}
void solve(int k)
{
    sort(tmp+1,tmp+t+1,cmp2);
    sum=0;
    memset(cnt,0,sizeof cnt);
    int L=k*K,R=L+1;
    for(int i=1;i<=t;i++)
    {
        memset(cnt2,0,sizeof cnt2);
        while(R<=q[tmp[i]].r)
        {
            sum+=cnt[c[R]];cnt[c[R]]++;R++;
        }
        s=sum;
        for(int j=L;j>=q[tmp[i]].l;j--)
        {
            s+=cnt[c[j]]+cnt2[c[j]];cnt2[c[j]]++;
        }
        q[tmp[i]].ans=s;
        q[tmp[i]].ans2=C(q[tmp[i]].r-q[tmp[i]].l+1);
        yf(q[tmp[i]]);
    }
}
int main()
{
    n=rd();m=rd();K=sqrt(n);
    for(int i=1;i<=n;i++)c[i]=rd();
    for(int i=1;i<=m;i++)q[i].l=rd(),q[i].r=rd(),rk[i]=i;
    sort(rk+1,rk+m+1,cmp);
    for(int i=1;(i-1)*K<n;i++)
    {
        t=0;
        while(q[rk[ct]].l<=i*K&&ct<=m)tmp[++t]=rk[ct],ct++;
        solve(i);
    }
    for(int i=1;i<=m;i++)
        printf("%lld/%lld
",q[i].ans,q[i].ans2);
    return 0;
}

 然后看了看题解,竟然是另一种做法,处理了一下式子:https://www.cnblogs.com/MashiroSky/p/5914637.html

所以抄了一下:

技术分享图片
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=50005;
int n,m,K,c[maxn],cnt[maxn],blk[maxn];
ll ans;
struct N{int l,r,bh;ll a,b;}q[maxn];
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<0||ch>9)ch=getchar();
    while(ch>=0&&ch<=9)ret=ret*10+ch-0,ch=getchar();
    return ret;
}
bool cmp(N x,N y){return blk[x.l]==blk[y.l]?x.r<y.r:blk[x.l]<blk[y.l];}
bool cmp2(N x,N y){return x.bh<y.bh;}
ll gcd(ll a,ll b){return a%b==0?b:gcd(b,a%b);}
void update(int x,int val)
{
    ans-=cnt[c[x]]*cnt[c[x]];
    cnt[c[x]]+=val;
    ans+=cnt[c[x]]*cnt[c[x]];
}
int main()
{
    n=rd();m=rd();K=sqrt(n);
    for(int i=1;i<=n;i++)c[i]=rd(),blk[i]=(i-1)/K+1;;
    for(int i=1;i<=m;i++)q[i].l=rd(),q[i].r=rd(),q[i].bh=i;
    sort(q+1,q+m+1,cmp);
    for(int i=1,l=1,r=0;i<=m;i++)
    {
        while(l<q[i].l)update(l,-1),l++;    while(l>q[i].l)update(l-1,1),l--;
        while(r<q[i].r)update(r+1,1),r++;    while(r>q[i].r)update(r,-1),r--;
        if(q[i].l==q[i].r)
        {
            q[i].a=0;q[i].b=1;continue;
        }
        q[i].a=(ll)ans-(r-l+1);    q[i].b=(ll)(r-l+1)*(r-l);//(ll)!!!
        ll k=gcd(q[i].a,q[i].b);//把分子放前面,万一分子是0 
        q[i].a/=k; q[i].b/=k;
    }
    sort(q+1,q+m+1,cmp2);
    for(int i=1;i<=m;i++)
        printf("%lld/%lld
",q[i].a,q[i].b);
    return 0;
}
...

然后又看到一篇博客:https://www.cnblogs.com/xuwangzihao/p/5199174.html

我的想法还是可以的嘛,加入一个点就是增加了之前有的这种点个数那么多的点对,所以维护点的个数即可;

主要是这个题不用严格按照分块来做,只是按分块排一下序就可以保证时间复杂度了,所以 l 和 r 直接全局移动就可以;

这样的话代码突然变得好优美...说到底自己那样的分块还是写得太丑,都不能保证正确呢...

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=50005;
int n,m,K,c[maxn],cnt[maxn],blk[maxn];
ll ans;
struct N{int l,r,bh;ll a,b;}q[maxn];
int rd()
{
    int ret=0;char ch=getchar();
    while(ch<0||ch>9)ch=getchar();
    while(ch>=0&&ch<=9)ret=ret*10+ch-0,ch=getchar();
    return ret;
}
bool cmp(N x,N y){return blk[x.l]==blk[y.l]?x.r<y.r:blk[x.l]<blk[y.l];}
bool cmp2(N x,N y){return x.bh<y.bh;}
ll C(ll x){return x*(x-1)/2;}
ll gcd(ll a,ll b){return a%b==0?b:gcd(b,a%b);}
void pop(int x){cnt[c[x]]--;ans-=cnt[c[x]];}//注意顺序 
void push(int x){ans+=cnt[c[x]];cnt[c[x]]++;}
int main()
{
    n=rd();m=rd();K=sqrt(n);
    for(int i=1;i<=n;i++)c[i]=rd(),blk[i]=(i-1)/K+1;
    for(int i=1;i<=m;i++)q[i].l=rd(),q[i].r=rd(),q[i].bh=i;
    sort(q+1,q+m+1,cmp);
    for(int i=1,l=1,r=0;i<=m;i++)
    {
        while(l<q[i].l)pop(l),l++;    
        while(l>q[i].l)push(l-1),l--;
        while(r<q[i].r)push(r+1),r++;    
        while(r>q[i].r)pop(r),r--;
        q[i].a=ans;
        q[i].b=C(r-l+1);
        ll k=gcd(q[i].a,q[i].b);//把分子放前面,万一分子是0 
        q[i].a/=k; q[i].b/=k;
    }
    sort(q+1,q+m+1,cmp2);
    for(int i=1;i<=m;i++)
        printf("%lld/%lld
",q[i].a,q[i].b);
    return 0;
}

 

以上是关于bzoj2038 小Z的袜子(hose)——莫队算法的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ_2038_[2009国家集训队]小Z的袜子(hose)_莫队

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

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

[bzoj] 2038 小Z的袜子(hose) || 莫队

bzoj2038 小Z的袜子(hose)——莫队算法

kyeremal-bzoj2038-[2009国家集训队]-小z的袜子(hose)-莫队算法