题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2038
解题思路:
莫队模板题。
对讯问排序,用到分块,
排序第一关键字为区间左端所在块
第二关键字为区间右的大小
至于为什么按照块排,数学上应该可以证明这么分复杂最小。
对于区间左右的延伸或者缩小。
只要不发生区间左比右大,右比左大就可以了。
所以我是先扩展,再收缩。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#include<cmath>
using namespace std;
const int N = 5e4+5;
int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);}
int blo,bl[N],n,m;
struct node
{
int l,r;
int id;
bool operator < (const node& a)const{
return (bl[l]<bl[a.l])||(bl[l]==bl[a.l] && r<a.r);
}
}q[N];
int l[N],r[N];
int cnt[N],ans[N],col[N];///当前颜色为i的袜子总数,答案,第i双袜子的颜色
int sum;///当前可以配对的总方法数
void Remove(int x)
{
cnt[col[x]]--;
sum -= cnt[col[x]];
}
void Add(int x)
{
sum += cnt[col[x]];
cnt[col[x]]++;
}
void solve()
{
int nowl = 1,nowr = 0;
for (int i=1;i<=m;i++){
int tl = q[i].l,tr = q[i].r;
while (nowr<tr) Add(++nowr);///先两边扩展好再收缩
while (nowl>tl) Add(--nowl);
while (nowr>tr) Remove(nowr--);
while (nowl<tl) Remove(nowl++);
ans[q[i].id] = sum;
}
}
int main()
{
scanf("%d %d",&n,&m);
blo = sqrt(n);
for (int i=1;i<=n;i++) scanf("%d",col+i),bl[i] = (i-1)/blo+1;
for (int i=1;i<=m;i++){
scanf("%d %d",&q[i].l,&q[i].r);
l[i] = q[i].l;
r[i] = q[i].r;
q[i].id = i;
}
sort(q+1,q+1+m);
solve();
for (int i=1;i<=m;i++){
ll fenzi = ans[i];
//printf("ans[%d]=%d\n",i,ans[i]);
if (ans[i]==0){
printf("0/1\n");
}
else {
ll fenmu = (ll)(r[i]-l[i]+1)*(r[i]-l[i])/2;///这里不用longlong会爆掉 50000*50000=25 0000 0000
ll gg = gcd(fenzi,fenmu);
fenzi /= gg;
fenmu /= gg;
printf("%lld/%lld\n",fenzi,fenmu);
}
}
return 0;
}