トピックへのリンク:https://www.lydsy.com/JudgeOnline/problem.php?id=2038
問題解決のアイデア:
Moのテンプレートチームのタイトル。
尋問は、ソート、ブロックを使用しました
最初のセクションでは、左側のブロックソートキーであります
2番目のキーは、右の大きさの範囲であります
理由としては、ブロック列ごとに、数学的に非常に複雑サブ最小を証明することができなければなりません。
左用と右のセクションを拡大または縮小。
限り間隔がクロージングよりも左のように、上の左の大きなより右を発生しません。
だから私は、最初の拡張、収縮しました。
コード:
#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;
}