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

似乎莫对算法才是正确的的姿势
不过直接分块暴力也可以
需要与处理几个东西和作诗那个题非常的相似,只不过那个题是强制在线的
维护到从开始每一个块的颜色的前缀和,块到块之间的答案
询问的时候,不跨过块就排序暴力判断,跨过块就先统计整块的答案,然后把两边的的单独排序暴力更新答案
学了莫队之后似乎就要换个姿势了

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int sc()
{
    int i=0;char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i;
}
int block_ans[233][233],block_sum[233][50005],bl[50005];
int a[50005],tim[50005],f[50005],st[5005];
long long C[50005];
int n,m,block,TI,col;
long long gcd(ll x,ll y){return x==0?y:gcd(y%x,x);}
void print(long long x,long long y)
{
    if(x==0||y==0)puts("0/1");
    else
    {
        long long c=gcd(x,y);
        printf("%lld/%lld\n",x/c,y/c);
    }
}       
int main()
{
    n=sc();m=sc();block=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        a[i]=sc();
        bl[i]=(i-1)/block+1;
        C[i]=(ll)i*(i-1)/2;
        col=max(col,a[i]);
    }
    for(int i=1;i*block+1<=n;i++)
    {
        TI++;
        for(int j=i*block+1;j<=n;j++)
        {
            int x=a[j];
            if(tim[x]!=TI)tim[x]=TI,f[x]=0;
            f[x]++;
            block_ans[i][bl[j]]+=C[f[x]]-C[f[x]-1];
            if(j%block==0)
            {
                block_ans[i][bl[j]]+=block_ans[i][bl[j]-1];
            }
        }
    }
    for(int i=1;i<=n;i++)
        block_sum[bl[i]][a[i]]++;
    for(int i=2;i*block+1<=n;i++)
        for(int j=0;j<=col;j++)
            block_sum[i][j]+=block_sum[i-1][j];
    for(int i=1;i<=m;i++)
    {
        int l=sc(),r=sc();
        if(bl[l]==bl[r])
        {
            int top=0,cnt=0;
            for(int j=l;j<=r;j++)st[++top]=a[j];
            sort(st+1,st+top+1);
            long long ans=0;
            for(int j=1;j<=top;j++)
            {
                cnt++;
                if(st[j]!=st[j+1]||j==top)
                    ans+=C[cnt],cnt=0;
            }
            print(ans,C[r-l+1]);
        }
        else
        {
            int top=0,cnt=0,x=bl[l],y=bl[r];
            long long ans=block_ans[x][y-1];
            for(int j=l;bl[j]==x;j++)st[++top]=a[j];
            for(int j=r;bl[j]==y;j--)st[++top]=a[j]; 
            sort(st+1,st+top+1);
            for(int j=1;j<=top;j++)
            {
                cnt++;
                if(st[j]!=st[j+1]||j==top)
                {
                    int u=st[j];
                    int temp=block_sum[y-1][u]-block_sum[x][u];
                    ans+=C[cnt+temp]-C[temp];
                    cnt=0;
                }
            }
            print(ans,C[r-l+1]);
        }
    }
    return 0;
}

补上莫队算法
为什么这么快–?
这里写图片描述
和普通分块相差整整5s 时间只是普通分块的一个零头
果然是黑科技啊,代码简单,越看越像暴力,然而复杂度就是n*sqrt(n)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int sc()
{
    int i=0;char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i;
}
struct W{int l,r,pos;}q[50005];
struct E{long long x,y;}ans[50005];
int a[50005],sum[50005],bl[50005];
int n,m,now=0,l,r,block;
bool cmp(W a,W b){return bl[a.l]==bl[b.l]?a.r<b.r:bl[a.l]<bl[b.l];}
long long gcd(ll x,ll y){return x==0?y:gcd(y%x,x);}
void print(long long x,long long y)
{
    if(x==0||y==0)puts("0/1");
    else
    {
        long long c=gcd(x,y);
        printf("%lld/%lld\n",x/c,y/c);
    }
}   
int main()
{
    n=sc();m=sc();block=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        a[i]=sc();
        bl[i]=(i-1)/block+1;
    }
    for(int i=1;i<=m;i++)q[i]=(W){sc(),sc(),i};
    sort(q+1,q+m+1,cmp);
    l=r=q[1].l;
    sum[a[q[1].l]]=1;
    for(int i=1;i<=m;i++)
    {
        while(l<q[i].l)now-=(--sum[a[l++]]);
        while(r>q[i].r)now-=(--sum[a[r--]]);
        while(l>q[i].l)now+=(sum[a[--l]]++);
        while(r<q[i].r)now+=(sum[a[++r]]++);
        ans[q[i].pos]=(E){now,(ll)(r-l+1)*(r-l)/2};
    }
    for(int i=1;i<=m;i++)print(ans[i].x,ans[i].y);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值