【莫队算法】bzoj2038 小z的袜子

题意:作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L < R)的这R-L+1只袜子中随机抽出两只穿上。尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

简化版:有一个长度为n的序列,m次询问,每次问在一个区间随机取两个位置,值相同的概率。
n,m<=50000。

算是莫队入门吧,这门入的。
莫队算法可用于解决一类可离线且在得到区间[l,r]的答案后,能在O(1)或O(log2n)得到区间[l,r+1]或[l−1,r]的答案的问题。有一些区间询问,并且在知道[l,r]的信息时可以快速求出[l±1,r]和[l,r±1]。将序列分成根号块,对区间进行排序,第一关键字是l所在块,第二关键字是r。按顺序处理每个区间,每次从上一个区间暴力移动左右端点。左端点会移动O(msqrtn)次,右端点会移动O(nsqrtn)次。
个人理解 是将询问分块,按一定的关键字排序可以使得左右端点的移动次数符合时间限制。从一块中通过移动左右端点拓展下一个询问。
关于莫队的时间复杂度和具象成最小曼哈顿生成树的玩意儿,哇,还不会。。。。。

题解:
相当于求相同数的对数。
莫队,维护每个数出现的次数。
在这里插入图片描述
v表示颜色 f表示出现的次数。
在这里插入图片描述
所以每加入一个不在当前区间的数的贡献为f[x]*(f[x]-1)/2
同时加或减 数出现的次数。
ans表示答案。 注意f[0] 的左端点初值。

#include<bits/stdc++.h>
   using namespace std;
const int MAXN=500000;
struct question{
   int l,r,b,id;
}q[MAXN];
int n,m,blo,a[MAXN],num[MAXN];
long long C,J1[MAXN],J2[MAXN],ans;
bool myc(question x,question y)
{
   if(x.b!=y.b)  return x.b<y.b;
   return x.r<y.r;
}
void del(int l,int r)
{
    for(int i=l;i<=r;i++)
    {
  	  int x=a[i];
  	  ans-=(1LL*num[x]*num[x]-num[x])/2;
  	  num[x]--;
  	  ans+=(1LL*num[x]*num[x]-num[x])/2;
    }
}
void add(int l,int r)
{
	for(int i=l;i<=r;i++)
	{
		int x=a[i];
		ans-=(1LL*num[x]*num[x]-num[x])/2;
		num[x]++;
		ans+=(1LL*num[x]*num[x]-num[x])/2;
	}
}
//bool myc2(question x,question y){ return x.id<y.id;}
int main()
{
    //freopen("2012.in","r",stdin);
	//freopen("2012.out","w",stdout);
	cin>>n>>m; blo=sqrt(n);
	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].b=(q[i].l+blo-1)/blo;  q[i].id=i;
 	}
 	sort(q+1,q+m+1,myc); q[0].l=1;
    for(int i=1;i<=m;i++)
    {
    // _______________________________________关键步骤。
      if(q[i].l<q[i-1].l) add(q[i].l,q[i-1].l-1);
      if(q[i].r>q[i-1].r) add(q[i-1].r+1,q[i].r);
      if(q[i].l>q[i-1].l) del(q[i-1].l,q[i].l-1);
      if(q[i].r<q[i-1].r) del(q[i].r+1,q[i-1].r);
      int len=(q[i].r-q[i].l+1);
      if(ans==0) J2[q[i].id]=1; 
	    else J2[q[i].id]=(1LL*len*len-len)/2;
      J1[q[i].id]=ans;  
      long long GCD=__gcd(J1[q[i].id],J2[q[i].id]);
      J1[q[i].id]/=GCD;  J2[q[i].id]/=GCD;
	}
//	sort(q+1,q+m+1,myc2);
	for(int i=1;i<=m;i++)  printf("%lld/%lld\n",J1[i],J2[i]);
    return 0;	
} 

题解来自
https://www.cnblogs.com/hzf-sbit/p/4056874.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值