C112 莫隊演算法 P1494 [國家集訓隊] 小 Z 的襪子

董晓發表於2024-04-05

影片連結:

Luogu P1494 [國家集訓隊] 小 Z 的襪子

// 普通莫隊 O(n*sqrt(n))
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

const int N=50005;
int n,m,B,a[N];
int sum,cnt[N],ans1[N],ans2[N];
struct Q{
  int l,r,id;
  //按l所在塊的編號l/B和r排序
  bool operator<(const Q &x) const{
    if(l/B!=x.l/B) return l<x.l;
    if((l/B)&1) return r<x.r;
    return r>x.r;
  }
}q[N];

void add(int x){
  sum+=cnt[x]; //當前x與前面每個x組合成雙
  cnt[x]++;    //x的出現次數
}
void del(int x){
  cnt[x]--; 
  sum-=cnt[x];
}
int gcd(int a,int b){
  return b?gcd(b,a%b):a;
}
int main(){
  scanf("%d%d",&n,&m);
  B=sqrt(n); //塊的大小
  for(int i=1;i<=n;i++) //顏色
    scanf("%d",&a[i]);
  for(int i=1;i<=m;i++) //詢問
    scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
  sort(q+1,q+m+1);
  for(int i=1,l=1,r=0;i<=m;i++){
    if(q[i].l==q[i].r){
      ans1[q[i].id]=0,ans2[q[i].id]=1;
      continue;
    }
    while(l>q[i].l)add(a[--l]);//左擴充套件
    while(r<q[i].r)add(a[++r]);//右擴充套件
    while(l<q[i].l)del(a[l++]);//左刪除
    while(r>q[i].r)del(a[r--]);//右刪除
    ans1[q[i].id]=sum;
    ans2[q[i].id]=1ll*(r-l+1)*(r-l)/2;
  }
  for(int i=1;i<=m;i++){
    if(ans1[i]){
      int g=gcd(ans1[i],ans2[i]);
      ans1[i]/=g,ans2[i]/=g; //最簡分數
    }
    else ans2[i]=1;
    printf("%d/%d\n",ans1[i],ans2[i]);
  }
}

相關文章