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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值