一种超级暴力的离线算法。。
对于只有区间询问,并且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; }