BZOJ-2038: [2009国家集训队]小Z的袜子(hose)

[文章目录]

Description

具体来说,小Z把这N只袜子从1N编号,然后从编号LR(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

看见出题人就瞬间知道用什么算法了,哈哈哈哈。第一道莫队。

莫队思想写到了另一道题上就不说了,每次由[L,R]计算到其他变动1的区间。

当区间变动时,将分子改变,使之始终等于可能抽到相同的袜子的方案数,分母就是总方案数,再/gcd就好啦。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
ll n,m,num[51000],c[51000],fz,fm=1;
ll gcd(ll x,ll y)
{
	return (y==0) ? x:gcd(y,x%y);
}
struct node
{
	ll l,r,block,fz,fm,id;
}a[50100];
bool cmp(node x,node y)
{
	return x.block==y.block ? x.r < y.r : x.block<y.block;
}
bool cmp1(node x,node y)
{
	return x.id<y.id;
}
void cut_L(ll &L) {fz+=2*num[c[L-1]]; num[c[L-1]]++;L--; }
void add_R(ll &R) {fz+=2*num[c[R+1]]; num[c[R+1]]++;R++; }
void add_L(ll &L) {num[c[L]]--; fz-=2*num[c[L]];L++; } 
void cut_R(ll &R) {num[c[R]]--; fz-=2*num[c[R]];R--; }
int main()
{
	scanf("%lld%lld",&n,&m);
	ll k1=(ll)sqrt(1.0*n+0.5);
	for(ll i=1;i<=n;i++) scanf("%lld",&c[i]);
	for(ll i=1;i<=m;i++)
	{
		scanf("%lld%lld",&a[i].l,&a[i].r);
		a[i].block=(a[i].l-1)/k1+1;a[i].id=i;
	}
	sort(a+1,a+m+1,cmp);
	ll L=1,R=0;
	for(ll i=1;i<=m;i++)
	{
		while(R<a[i].r) add_R(R);
		while(L>a[i].l) cut_L(L);
		while(L<a[i].l) add_L(L);
		while(R>a[i].r) cut_R(R);
		fm=(R-L+1)*(R-L);
		ll tmp=gcd(fz,fm);
		a[i].fz=fz/tmp,a[i].fm=fm/tmp;
	}
	sort(a+1,a+m+1,cmp1);
	for(ll i=1;i<=m;i++)
		printf("%lld/%lld\n",a[i].fz,a[i].fm);
	return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注