莫队——[国家集训队]小Z的袜子

题目链接

题目描述

作为一个生活散漫的人,小 Z 每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小 Z 再也无法忍受这恼人的找袜子过程,于是他决定听天由命……

具体来说,小 Z 把这 N 只袜子从 1 到 N 编号,然后从编号 L 到 R (L 尽管小 Z 并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。

你的任务便是告诉小 Z,他有多大的概率抽到两只颜色相同的袜子。当然,小 Z 希望这个概率尽量高,所以他可能会询问多个 (L,R) 以方便自己选择。

然而数据中有 L=R 的情况,请特判这种情况,输出0/1。

输入格式

输入文件第一行包含两个正整数 N 和 M。N 为袜子的数量,M 为小 Z 所提的询问的数量。接下来一行包含 N 个正整数 Ci,其中 Ci 表示第 i 只袜子的颜色,相同的颜色用相同的数字表示。再接下来 M 行,每行两个正整数 L, R 表示一个询问。

输出格式

包含 M 行,对于每个询问在一行中输出分数 A/B 表示从该询问的区间 [L,R] 中随机抽出两只袜子颜色相同的概率。若该概率为 0 则输出 0/1,否则输出的 A/B 必须为最简分数。(详见样例)

输入输出样例

输入

6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6

输出

2/5
0/1
1/1
4/15

说明/提示

30%的数据中,N,M ≤ 5000;
60%的数据中,N,M ≤ 25000;
100%的数据中,N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

分析

设不同颜色的袜子个数分别为 a,b,c…,然后我们就可以推出
在这里插入图片描述
从这个公式中,我们可以看出最后的结果都和不同颜色的袜子个数的平方和有关,那么我们在新增或者删除的时候,都要考虑一下新增或删除的某种袜子的个数的平方和的变化,而每次查询的 L,R 是固定的,因此,可以在考虑完平方和问题之后再对分子分母进行相关操作,然后再求出分子分母的最大公约数,化简即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int n, m, L, R;
long long sum;
int a[maxn], f[maxn], cnt[maxn];
long long ans1[maxn], ans2[maxn];

struct node{
	int l, r, id;
}q[maxn];

bool cmp(node x,node y){
	return f[x.l] ^ f[y.l] ? f[x.l] < f[y.l] : ((f[x.l] & 1) ? x.r < y.r : x.r > y.r);
}

int main()
{
	scanf("%d%d", &n, &m);
	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;
	}
	int unit = sqrt(n);
	for(int i = 1; i <= n; i++){
		f[i] = i / unit + 1;
	}
	sort(q + 1, q + m + 1, cmp);
	L = q[1].l, R = q[1].l - 1;
	sum = 0;
	for(int i = 1; i <= m; i++){
		while(L < q[i].l){
			sum -= cnt[a[L]] * cnt[a[L]];
			cnt[a[L]]--;
			sum += cnt[a[L]] * cnt[a[L]];
			L++;
		}
		while(L > q[i].l){
			L--;
			sum -= cnt[a[L]] * cnt[a[L]];
			cnt[a[L]]++;
			sum += cnt[a[L]] * cnt[a[L]];
		}
		while(R < q[i].r){
			R++;
			sum -= cnt[a[R]] * cnt[a[R]];
			cnt[a[R]]++;
			sum += cnt[a[R]] * cnt[a[R]];
		}
		while(R > q[i].r){
			sum -= cnt[a[R]] * cnt[a[R]];
			cnt[a[R]]--;
			sum += cnt[a[R]] * cnt[a[R]];
			R--;
		}
		if(q[i].l == q[i].r){
			ans1[q[i].id] = 0;
			ans2[q[i].id] = 1;
			continue;
		}
		ans1[q[i].id] = sum - (q[i].r - q[i].l + 1);
		ans2[q[i].id] = 1LL * (q[i].r - q[i].l + 1) * (q[i].r - q[i].l);
		long long g = __gcd(ans1[q[i].id], ans2[q[i].id]);
		ans1[q[i].id] /= g;
		ans2[q[i].id] /= g;
	}
	for(int i = 1; i <= m; i++){
		if(ans1[i] == 0){
			printf("0/1\n");
		}
		else{
			printf("%lld/%lld\n", ans1[i], ans2[i]);
		}
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值