给一个我认为数据比较强的题目链接:AcWing 251. 小Z的袜子
题意:询问区间内抽到相同颜色的概率
如果时间多一点的话是可以有一种在线分块做法的,感兴趣可以看一下这个:第十四个
莫队是个板子,记住就好了。
基本思想是先将序列分块,然后对于所有询问,按照L属于的块编号从小到大,以及第二关键字r从小到大进行排序。
然后就是如果我们知道了区间[L,R]的答案,我们可以在O(1)或者O(logn)的时间内转移到[L,R+1],[L,R-1],[L-1,R],[L+1,R]。
贴一个讲的比较好的帖子:知乎
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+7;
int val[maxn];
int c[maxn];
ll res=0;
int l=1,r=0;
void init(){
l=1;
r=0;
res=0;
}
void del(int id){
res-=2LL*val[id]-1;
val[id]--;
}
void add(int id){
res+=2LL*val[id]+1;
val[id]++;
}
struct ASK{
int l,r;
int id;
int belong;
bool operator <(const ASK& x)const{
if(belong!=x.belong) return belong<x.belong;
return r<x.r;
}
}ask[maxn];
ll fz[maxn],fm[maxn];
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
int main(){
init();
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d",&c[i]);
int p=sqrt(n);
for(int i=1;i<=m;++i){
scanf("%d%d",&ask[i].l,&ask[i].r);
ask[i].id=i;
int z=ask[i].l/p;
int mod=ask[i].l%p;
ask[i].belong=z+(mod?1:0);
}
sort(ask+1,ask+1+m);
for(int i=1;i<=m;++i){
while(r<ask[i].r) add(c[++r]);
while(r>ask[i].r) del(c[r--]);
while(l<ask[i].l) del(c[l++]);
while(l>ask[i].l) add(c[--l]);
ll xx=res-r+l-1;
if(xx){
ll yy=(r-l+1LL)*(r-l);
ll zz=gcd(xx,yy);
fz[ask[i].id]=xx/zz;
fm[ask[i].id]=yy/zz;
}
else{
fz[ask[i].id]=0;
fm[ask[i].id]=1;
}
}
for(int i=1;i<=m;++i)
printf("%lld/%lld\n",fz[i],fm[i]);
return 0;
}