----普通莫队

首先
清楚概率怎么求
假设我们要求从区间l到r中拿出一对袜子的概率
sum[i]为第i种袜子在l到r中的数量

$$\frac{\sum_{i=l}^{r} {[sum[i] \times (sum[i]-1)]}}{ (r-l+1) \times {(r-l)}}\qquad$$

转化一下可以得到

$$\frac{\sum_{i=l}^{r} {sum[i]^{2}}-(r-l+1)}{ (r-l+1)\times {(r-l)}}\qquad$$

普通莫队是一种离线算法 并且充分利用上一个得到的答案来求得当前询问的答案

怎么由上一个答案来得到当前的答案呢?

$$ans=\sum_{i=l}^{r} {sum[i]^{2}}$$

即分母的一部分(减去(r-l+1)即可得到分母)

现在要求[l+1,r]这个区间的答案
若第l只袜子的编号为x 则只有sum[x]减少了1
更新ans的操作如下:
ans-=sum[x]*sum[x];
sum[x]-=1;
ans+=sum[x]*sum[x]
即:减去从前对答案的贡献 加上现在的贡献
求[l-1,r],[l,r-1],[l,r+1]的做法类比可得
整理一下可以得到change函数

//x为新增加或减少的点
//若为新增加的点 如l指针左移和r指针右移 则w=1
//反之 w=-1
/*例子:求[l-1,r] change(l,-1)
求[l,r+1] change(r+1,1) ……*/
change(int x,int w)
{
ans-=sum[x]*sum[x];sum[x]+=w;ans+=sum[x]*sum[x];
}

 

为了使l和r指针尽可能少的移动(优化时间)
我们需要给所有的问题的l和r排序

构造cmp函数
要用分块

将整个长度为n的序列分为sqrt(n)块
cmp为:若l与r在同一块中 则按照l从小到大排序
      否则 按照r从小到大排序
这样l指针每次最多跳2*(n/sqrt(n))次
最后求gcd 即可
还要记得l==r的特判

一种优秀的gcd求法

//普通gcd
int gcd(int x,int y)
{
return y==0?x:gcd(y,x%y);
}

其实可以看成是把x赋值为x%y 再调换x,y的位置 求gcd(x,y)
假设我们要调换a,b的值

//普通做法:
int tmp;tmp=a;a=b;b=tmp; //其实可以利用位运算 异或
x^=y;y^=x;x^=y; /* 第一步:x=x^y
第二步:y=y^(x^y) 由于y^y=0 所以 y=x
第三步:x=(x^y)^x 同理可得 x=y
*/

可以得到gcd函数

ll gcd(ll a,ll b)
{
while(b^=a^=b^=a%=b);return a;
}
//从右至左运算

分析over

code
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
#define yes(i,a,b) for(register int i=a;i<=b;i++)
#define M 50010
using namespace std;
int n,m,tot,len;
ll ans,f_ans[M][2],sum[M];
int c[M],be[M];
struct node {int l,r,id;} q[M];
bool cmp(node x,node y)
{
if(be[x.l]==be[y.l]) return x.r<y.r;
return x.l<y.l;
}
void change(int x,int w)
{
ans-=(ll)(sum[c[x]]*sum[c[x]]);sum[c[x]]+=w;ans+=(ll)(sum[c[x]]*sum[c[x]]);
}
ll gcd(ll a,ll b)
{
while(b^=a^=b^=a%=b);return a;
}
int main()
{
//freopen("1.in","r",stdin);
//freopen("1.out","w",stdout);
scanf("%d%d",&n,&m);
len=sqrt(n);
yes(i,1,n) scanf("%d",&c[i]),be[i]=i/len+1;
yes(i,1,m) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+m+1,cmp);
int l=1,r=0;
yes(i,1,m)
{
while(l<q[i].l) change(l,-1),l++;
while(l>q[i].l) change(l-1,1),l--;
while(r<q[i].r) change(r+1,1),r++;
while(r>q[i].r) change(r,-1),r--;
ll ans1,ans2;
ans1=ans-(ll)(q[i].r-q[i].l+1);ans2=(ll)(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
if(ans1==0) ans2=1;
else
{
ll g=gcd(ans2,ans1);
ans1/=g;ans2/=g;
}
f_ans[q[i].id][0]=ans1;f_ans[q[i].id][1]=ans2;
}
yes(i,1,m) printf("%lld/%lld\n",f_ans[i][0],f_ans[i][1]);
return 0;
}

最新文章

  1. [.NET] WebApi 生成帮助文档及顺便自动创建简单的测试工具
  2. android view :事件
  3. Gradle常用命令
  4. 简进祥===AFNetWorking 下载视频文件
  5. Centos7下搭建LAMP平台环境 (转载)
  6. 济南学习D2T1__折纸带
  7. oracle中,拼接的字符串给游标赋值
  8. Java 使用jaxp添加节点
  9. listview定位到上次显示的位置
  10. ios 开发 收起键盘的小技巧
  11. 一分钟加入google站内搜索代码
  12. puppet中anchor的作用
  13. MySQL安装完可以使用,但是找不到对应的系统服务
  14. 【转】aiohttp 源码解析之 request 的处理过程
  15. continue与break
  16. Charles使用(二)
  17. Google 视频编码格式 VP9 究竟厉害在哪里
  18. [微信小程序] 通过快速启动demo分析小程序入门关键点
  19. Qt编写自定义控件2-进度条标尺
  20. 2017ACM/ICPC广西邀请赛-重现赛

热门文章

  1. markdown语法---根据使用不断扩充中
  2. 在Google Chrome中快速解除网页屏蔽鼠标右键、复制等限制
  3. NULL,&quot;&quot;,String.Empty三者在C#中的区别
  4. NOI前训练日记
  5. There is no getter for property named &#39;notice&#39; in &#39;class com.game.domain.Notices&#39;
  6. MT【136】一道三次函数的最佳逼近问题
  7. Spring Security OAuth 个性化token
  8. 模板:快速傅里叶变换(FFT)
  9. BZOJ1150:[APIO/CTSC2007]数据备份——题解
  10. Java之高级IO,Properties