P1494 小Z的袜子


莫队

莫队可以快速找到某区间的元素个数,元素最大值,最小值……

遍历莫队

举个小例子:
      1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
设: l为左边界,r为右边界,sum为元素个数,shu为现有的数存的数组(假设数据很小)
        
求1,2区间分别出现次数最多的数的和(若一样多,以最小的数为例)。
1区间:0~6
2区间:3~8
    
先来求1区间出现最多次的数:
数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点 l
右端点 r
sum=1,shu[1]++;(shu[1]=1)
      
数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点 l
右端点   r
sum=2,shu[3]++;(shu[3]=1)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点 l
右端点     r
sum=3,shu[4]++;(shu[4]=1)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点 l
右端点       r
sum=4,shu[2]++;(shu[2]=1)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点 l
右端点         r
sum=5,shu[3]++;(shu[3]=2)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点 l
右端点           r
sum=6,shu[4]++;(shu[4]=2)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点 l
右端点             r
sum=7,shu[2]++;(shu[2]=2)

上面可以发现,2,3,4都出现了两次,所以取2(按题意)
    
下面 找区域2,出现次数最多的数。

出现区域2次数最多的数

2区间:3~8

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点 l
右端点             r
sum=7,shu[2]++;(shu[2]=2)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点   l
右端点             r
sum=6,shu[1]--;(shu[1]=0)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点     l
右端点             r
sum=5,shu[3]--;(shu[3]=1)
   
数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点       l
右端点             r
sum=4,shu[4]--;(shu[4]=1)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点       l
右端点               r
sum=5,shu[3]++;(shu[3]=2)

数列: 1 3 4 2 3 4 2 3 6 7 8
下标: 0 1 2 3 4 5 6 7 8 9 10
左端点       l
右端点                 r
sum=6,shu[6]++;(shu[6]=1)
        
由上述可以得到2,3次数一样多取2
综上,答案呼之欲出了,2+2=4。

回归

因为有好几组询问,所以每回莫队都是要变得,通过上一个结果,该加的加,该减的减,再通过数学的$C^n_m$运算,就可以搞定了

传送门

代码

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstdio>
#include<cctype>
#include<queue>
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
int n,m,pp[50005],c[50005];
long long s[50005],ans;
struct data{
    int l,r,id;
    long long a,b;
}a[50005];
bool cmp(const data & a,const data & b){
    if(pp[a.l]==pp[b.l])
    return a.r<b.r;
    return a.l<b.l;
}
bool ccmp(const data & a,const data & b){
    return a.id<b.id;
}
void update(int p,int add){
    ans-=s[c[p]]*s[c[p]];
    s[c[p]]+=add;
    ans+=s[c[p]]*s[c[p]];
}
void gai(){
    for(int i=1,l=1,r=0;i<=m;i++){
        for(;r<a[i].r;r++)update(r+1,1);
        for(;r>a[i].r;r--)update(r,-1);
        for(;l<a[i].l;l++)update(l,-1);
        for(;l>a[i].l;l--)update(l-1,1);
        if(a[i].l==a[i].r){
            a[i].a=0;
      a[i].b=1;
            continue;
        }
        a[i].a=ans-(a[i].r-a[i].l+1);
        a[i].b=(a[i].r-a[i].l+1)*1ll*(a[i].r-a[i].l);
        long long g=__gcd(a[i].a,a[i].b);
        a[i].a/=g;
    a[i].b/=g;
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%d",&c[i]);
    int b_b=sqrt(n);
    for(int i=1;i<=n;i++)pp[i]=(i-1)/b_b+1;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&a[i].l,&a[i].r);
        a[i].id=i;
    }
    sort(a+1,a+m+1,cmp);
    gai();
    sort(a+1,a+m+1,ccmp);
    for(int i=1;i<=m;i++)
        printf("%lld/%lld\n",a[i].a,a[i].b);
    return 0;
}

文章作者: 王大神——A001
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 王大神——A001 !
  目录