您的位置:首页 > 其它

[BZOJ2038][2009国家集训队]小Z的袜子(hose)(莫队)

2016-02-23 20:24 387 查看

题目描述

传送门

题解

首先求概率的方法为:假设在LR这段区间里颜色为x,y,z…的袜子分别有a,b,c…个。

那么概率(利用排列组合推导)

p=a(a−1)2+b(b−1)2+c(c−1)2+…(R−L+1)(R−L)2

=a2+b2+c2+…−(a+b+c+…)(R−L+1)(R−L)

a2+b2+c2+…−(R−L+1)(R−L+1)(R−L)

那么我们用莫队算法+分块乱搞就能求出答案。O(nn√)

首先离线操作将区间排序,按照左端点的分块的编号,同一块内按照右端点排序。然后暴力更改区间求值即可。

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long

const int max_n=5e4+5;
const int max_m=5e4+5;
const int max_c=5e4+5;

struct hp{
LL l,r,block,num;
}f[max_m];
struct hq{
LL up,down;
}ans[max_m];
LL a[max_n],c[max_c];
LL n,nn,m,L,R,up,down,GCD,upans,downans;

inline int cmp(hp a,hp b){
return a.block<b.block||(a.block==b.block&&a.r<b.r)||(a.block==b.block&&a.r==b.r&&a.l<b.l);
}

inline LL gcd(LL a,LL b){
if (!b) return a;
else return gcd(b,a%b);
}

int main(){
scanf("%lld%lld",&n,&m);
nn=sqrt(n);
for (int i=1;i<=n;++i)
scanf("%lld",&a[i]);
for (int i=1;i<=m;++i){
scanf("%lld%lld",&f[i].l,&f[i].r);
f[i].num=i;
}
for (int i=1;i<=m;++i){
if (f[i].l%nn==0)
f[i].block=f[i].l/nn;
else f[i].block=f[i].l/nn+1;
}
sort(f+1,f+m+1,cmp);
L=R=1;
c[a[1]]=1;
up=0;
for (int i=1;i<=m;++i){
up+=R-L+1;

if (f[i].l<L)
for (int j=L-1;j>=f[i].l;--j){
up-=c[a[j]]*c[a[j]];
up+=(++c[a[j]])*c[a[j]];
}
if (f[i].r>R)
for (int j=R+1;j<=f[i].r;++j){
up-=c[a[j]]*c[a[j]];
up+=(++c[a[j]])*c[a[j]];
}
if (f[i].l>L)
for (int j=L;j<f[i].l;++j){
up-=c[a[j]]*c[a[j]];
up+=(--c[a[j]])*c[a[j]];
}
if (f[i].r<R)
for (int j=R;j>f[i].r;--j){
up-=c[a[j]]*c[a[j]];
up+=(--c[a[j]])*c[a[j]];
}

L=f[i].l,R=f[i].r;
up-=R-L+1;
down=(R-L+1)*(R-L);
if (!up){
ans[f[i].num].up=0;
ans[f[i].num].down=1;
continue;
}
GCD=gcd(up,down);
upans=up/GCD;
downans=down/GCD;
ans[f[i].num].up=upans;
ans[f[i].num].down=downans;
}
for (int i=1;i<=m;++i)
printf("%lld/%lld\n",ans[i].up,ans[i].down);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: