Evanyou Blog 彩带

P1494 [国家集训队]小Z的袜子

题目描述

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……

具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。

你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

然而数据中有L=R的情况,请特判这种情况,输出0/1。

输入输出格式

输入格式:

输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。

输出格式:

包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)

输入输出样例

输入样例#1: 
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
输出样例#1: 
2/5
0/1
1/1
4/15

说明

30%的数据中 N,M ≤ 5000;

60%的数据中 N,M ≤ 25000;

100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

 

Solution:

  用这道题当作莫队的模板吧。(我这里不详细深究莫队的原理,只谈及一下自己的理解)莫队

  莫队其实就是优化的暴力。其实只有4个$while$循环,复杂度就体现在这4个循环的指针移动次数,而将进行分块并块内排序后能降低指针移动次数,起到优化作用。我们离线操作,读入查询的所有区间,分为$\sqrt{n}$块,每块中按$r$升序排列。

  在每块中进行暴力指针移动,对于指针$l,r$各自只存在左移和右移两种情况,每次移动1步,便重新维护一下信息(本题中的分子),处理出答案后保存一下,最后输出就$OK$了。

  回到本题,我们设$s[i]$表示$i$数字在区间$[l,r]$中出现的次数,容易得出区间$[l,r]$的抽到概率为:

  $$\frac{\sum s[i]*(s[i-1]-1)}{(l-r+1)(l-r)}=\frac{\sum s[i]^2-\sum s[i]}{(l-r+1)(l-r)}=\frac{\sum s[i]^2-(l-r+1)}{(l-r+1)(l-r)}$$

  于是我们只需维护一下$s[i]^2$,然后在每次指针移动后用分子减去原来的$s[i]^2$,更新$s[i]$,再加上新的$s[i]^2$,便求出新的分子,至于分母就是$(r-l+1)(r-l)$。

  然后求出分子和分母的最大公约数,约分一下并保存答案,最后输出就完美的解决了。

 

代码:

 

#include<bits/stdc++.h>
#define il inline
#define ll long long
using namespace std;
const int N=50005;
ll n,m,pos[N],a[N],ans,s[N],fen[N][2];
struct data{
    ll l,r,id;
}t[N];
il ll gi()
{
    ll a=0;char x=getchar();bool f=0;
    while((x<'0'||x>'9')&&x!='-')x=getchar();
    if(x=='-')x=getchar(),f=1;
    while(x>='0'&&x<='9')a=a*10+x-48,x=getchar();
    return f?-a:a;
}
il ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
il bool cmp1(data a,data b){return pos[a.l]==pos[b.l]?a.r<b.r:a.l<b.l;}
il void add(ll p,ll val)
{
    ans-=s[a[p]]*s[a[p]];
    s[a[p]]+=val;
    ans+=s[a[p]]*s[a[p]];
}
int main()
{
    n=gi(),m=gi();
    for(int i=1;i<=n;i++)a[i]=gi();
    int ss=int(sqrt(n));
    for(int i=1;i<=n;i++)pos[i]=i/ss;
    for(int i=1;i<=m;i++)t[i].l=gi(),t[i].r=gi(),t[i].id=i;
    sort(t+1,t+1+m,cmp1);
    for(int i=1,l=1,r=0;i<=m;i++){
        while(r<t[i].r)r++,add(r,1);
        while(r>t[i].r)add(r,-1),r--;
        while(l<t[i].l)add(l,-1),l++;
        while(l>t[i].l)l--,add(l,1);
        if(t[i].l==t[i].r){fen[t[i].id][0]=0,fen[t[i].id][1]=1;continue;}
        fen[t[i].id][0]=ans-(t[i].r-t[i].l+1);
        fen[t[i].id][1]=(ll)(t[i].r-t[i].l+1)*(t[i].r-t[i].l);
        ll k=gcd(fen[t[i].id][0],fen[t[i].id][1]);
        fen[t[i].id][0]/=k,fen[t[i].id][1]/=k;
    }
    for(int i=1;i<=m;i++)printf("%lld/%lld\n",fen[i][0],fen[i][1]);
    return 0;
}

 

 

 

 

 

posted @ 2018-04-10 19:06  five20  阅读(156)  评论(0编辑  收藏  举报
Live2D