一种超级暴力的离线算法。。

对于只有区间询问,并且ans[l,r]→ans[l,r+1],ans[l+1,r]都可以O(1)转移的题目,可以把所有询问分块排序,复杂度O(n√n)

排序也有技巧。

首先按照l所在块排序,如果l在同一块,则:

①若l在奇数块,按照r升序排序

②若l在偶数块,按照r降序排序

小Z的袜子是超经典的莫队裸题:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<climits>
#include<cstdio>
#include<cmath>
#ifdef WIN32
#define ll "%I64d"
#else
#define ll "%lld"
#endif
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=a;i>=b;i--)
typedef long long LL;//一开始提交总是RE,我就偷懒把所有int全改成long long了
const LL M=50010;
LL sum[M];
int c[M];
struct query{
    LL l,r,p,b;
    LL ans1,ans2;
}q[M];
LL read(){
    LL x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
bool cmp(query x,query y){//一升一降交错排序可以提高效率
    if(x.b==y.b){
        if(x.b&1)return x.r>y.r;
        else return x.r<y.r;
    }
    else return x.b<y.b;
}
bool cmp1(query x,query y){
    return x.p<y.p;
}
LL gcd(LL a,LL b){
    if(a==0)return b;
    return gcd(b%a,a);
}
int main(){
    LL n=read(),m=read();
    LL block=(LL)sqrt(n);
    rep(i,1,n)c[i]=read();
    rep(i,1,m){
        q[i].l=read();q[i].r=read();
        q[i].p=i;q[i].b=q[i].l/block;
    }
    sort(q+1,q+m+1,cmp);
    
    rep(i,q[1].l,q[1].r)sum[c[i]]++;
    rep(i,1,n)q[1].ans1+=1LL*sum[i]*(sum[i]-1);
    q[1].ans2=(LL)(q[1].r-q[1].l+1)*(q[1].r-q[1].l);//先求出第一个答案
    rep(i,2,m){
        LL j=q[i-1].l;LL s1=q[i-1].ans1;
        while(j>q[i].l){//O(1)转移
            sum[c[--j]]++;s1=s1+((sum[c[j]]-1)<<1);
        }
        while(j<q[i].l){
            sum[c[j]]--;s1=s1-(sum[c[j++]]<<1);
        }
        j=q[i-1].r;
        while(j>q[i].r){
            sum[c[j]]--;s1=s1-(sum[c[j--]]<<1);
        }
        while(j<q[i].r){
            sum[c[++j]]++;s1=s1+((sum[c[j]]-1)<<1);
        }
        q[i].ans1=s1;q[i].ans2=(LL)(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
    }
    sort(q+1,q+m+1,cmp1);
    rep(i,1,m){
        if(q[i].ans1==0){
            printf("0/1\n");continue;
        }
        LL d=gcd(q[i].ans1,q[i].ans2);
        printf(ll"/"ll"\n",q[i].ans1/d,q[i].ans2/d);
    }
    return 0;
}