1. 程式人生 > >bzoj2038 小Z的襪子(hose)——莫隊算法

bzoj2038 小Z的襪子(hose)——莫隊算法

題解 HA 圖片 r++ eof sed ide return pan

題目: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\n",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\n",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\n",q[i].a,q[i].b);
    return 0;
}

bzoj2038 小Z的襪子(hose)——莫隊算法