纪念自己独立完成的一道省选题(菜鸡如我只会看题解qwq)
还算是一道比较裸的莫队题把,比有的题目简单很多,也很好想怎么O(1)转移,比别的题就多了一个组合数计算(还有gcd??),还没算%意义下,也是很良心了

int get(int x){
	if(x<=1)return 0;
	return (int)(1.0*x*(x-1)*1.0/2.0);
}

直接获得当前的可能情况。
然后就是莫队的一套全家桶模板了。话说,莫队分块真的很玄学!
对了 题目还贴心的提示了特判问题 luogu不要太好。。!~~
这几天写了一些莫队题。莫队入门的话还是去写那些区间不重复数个数把,应该是最简单的题了(如果不卡莫队的话)然后对转移的add,del函数自己要推一下,然后套个全家桶就完事了

#include<bits/stdc++.h>
#define swap(a,b) {a^=b;b^=a;a^=b}
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(i,n,a) for (int i=n;i>=a;i--)
#define max(a,b) (a)>(b)?(a):(b)
#define min(a,b) (a)<(b)?(a):(b)
#define PII pair<int,int>
#define yxdl(qt) priority_queue<int,vector<int>,greater<int> >qt
#define xgd(qt) priority_queue<int,vector<int>,less<int> >qt
#define INF 0x3f3f3f3f
#define lowbit(x) x & -x
#define cs(s) freopen(s,"r",stdin)
#define debug(x) printf(" case-->%d ",x );
using namespace std;
typedef long long ll;
const int maxn =50101;
int a[maxn],flag[maxn],n,m,div1,l=0,r,up,down;
int gcd(int a,int b){
	return b==0?a:gcd(b,a%b);
}
int get(int x){
	if(x<=1)return 0;
	return (int)(1.0*x*(x-1)*1.0/2.0);
}
struct uzi
{
	int l,r,id;
	bool operator<(const uzi &a)const {
		return l/div1==a.l/div1?r<a.r:l<a.l;
	}
}p[maxn];
void del(int x){
	up-=get(flag[a[x]]);
	flag[a[x]]--;
	if(flag[a[x]]<=1)return;
	up+=get(flag[a[x]]);
}
void add(int x){
	up-=get(flag[a[x]]);
	flag[a[x]]++;
	up+=get(flag[a[x]]);
}
struct node
{
	int up,down;
}ans[maxn];
int main(){
	scanf("%d%d",&n,&m);
	rep(i,1,n)scanf("%d",&a[i]);
	div1=sqrt(n);
	rep(i,1,m){
		scanf("%d%d",&p[i].l,&p[i].r);
		p[i].id=i;
	}
	sort(p+1,p+1+m);
	int bj[maxn];
	int po=0;
	rep(i,1,m){
		if(p[i].l==p[i].r){ans[p[i].id].up=0;ans[p[i].id].down=1;bj[++po]=p[i].id;}
		else{
			while(l<p[i].l)del(l++);
			while(l>p[i].l)add(--l);
			while(r<p[i].r)add(++r);
			while(r>p[i].r)del(r--);
			ans[p[i].id].up=up;
			ans[p[i].id].down=get(r-l+1);
		}
	}
	rep(i,1,m){
		if(ans[i].up==0){puts("0/1");continue;}
		int qq=gcd(ans[i].up,ans[i].down);
		printf("%d/%d\n",ans[i].up/qq,ans[i].down/qq );
	}
}