bzoj 2038: [2009国家集训队]小Z的袜子(hose) (经典莫队题)

进行区间询问[l,r],输出该区间内随机抽两次抽到相同颜色袜子的概率。

一个区间可以选取的总数是 C(r-l+1,2), 这个是分母, 分子是 \sum(color,2) C(区间颜色,2)

ans 代表分子,是区间求和,每种颜色可以选择的可能。

莫队算法的复杂度是  n*sqrt(n);

#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 5e4+10;
int col[N],unit,Be[N];
struct Mo{
	int l,r,ID;
	LL A,B;
}f[N];
LL ans,sum[N];
LL GCD(LL a, LL b){
	if (b == 0) return a; else return GCD(b,a%b);
}
LL S(LL x){return x*(x-1);}
bool cmp(Mo const &aa, Mo const &bb){
	if (Be[aa.l] == Be[bb.l]) return aa.r < bb.r;
	return aa.l < bb.l;
}
bool CMP(Mo const &aa, Mo const &bb){return aa.ID < bb.ID;}
void action(int x, int y){ans -= S(sum[col[x]]); sum[col[x]] += y; ans+=S(sum[col[x]]);}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	unit = sqrt(n);
	go(i,1,n)scanf("%d",&col[i]),Be[i] = i/unit+1; //这个地方 莫队分块。
	go(i,1,m) scanf("%d%d",&f[i].l,&f[i].r),f[i].ID = i;
	sort(f+1,f+m+1,cmp);
	int l = 1,r = 0;
	go(i,1,m){
		while(l < f[i].l) action(l,-1),l++;  //好多 while 语句。
		while(l > f[i].l) action(l-1,1),l--;
		while(r < f[i].r) action(r+1,1),r++;
		while(r > f[i].r) action(r,-1),r--;
		if (f[i].l == f[i].r) {f[i].A = 0,f[i].B = 1;continue;}
		f[i].A = ans; f[i].B = S(f[i].r - f[i].l + 1);
		LL gcd = GCD(f[i].A,f[i].B);
		f[i].A /= gcd; f[i].B /= gcd;
	}
	sort(f+1,f+m+1,CMP);
	go(i,1,m) printf("%lld/%lld\n",f[i].A,f[i].B);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/kidsummer/article/details/81627995