小Z的袜子(莫队算法)

题目链接

Description


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


Input


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


Output


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


Sample Input

6 4

1 2 3 3 3 2

2 6

1 3

3 5

1 6


Sample Output


2/5

0/1

1/1

4/15


【样例解释】

询问1:共C(5,2)=10种可能,其中抽出两个2有1种可能,抽出两个3有3种可能,概率为(1+3)/10=4/10=2/5。

询问2:共C(3,2)=3种可能,无法抽到颜色相同的袜子,概率为0/3=0/1。

询问3:共C(3,2)=3种可能,均为抽出两个3,概率为3/3=1/1。

注:上述C(a, b)表示组合数,组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。


【数据规模和约定】

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

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

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


 

终于入门莫队算法了!!!好开心,但是还有另外两种(在线修改+树上),到时我再弄一弄。目前的莫队已经足够我使用了。

提供两种方法:但是莫队的模板是不变的,但是add( ) ,del ( ) 这个要自己写。

网上题解:

网上的这个是用公式来表示每一种颜色的袜子的抽到的次数,综合每一种颜色 /  所有情况

在这里写一遍吧:

在一段区域内,有多种颜色,每一种颜色出现的次数为:a,b,c,……x次

对于(a+b+c……x)相当于整个区间的

                                                               \frac{C_{a}^{2}+C_{b}^{2}+C_{c}^{2}+...+C_{x}^{2}}{C_{n}^{2}}

                                                              =\frac{a*\frac{a-1}{2}+b*\frac{b-1}{2}+c*\frac{c-1}{2}+....+x*\frac{x-1}{2}}{\frac{(R-L+1)*(R-L)}{2}}

                                                             =\frac{a^{2}+b^{2}+c^{2}+....+x^{2}-(a+b+c+...+x)}{(R-L+1)*(R-L)}

                                                             =\frac{a^{2}+b^{2}+c^{2}+....+x^{2}-(R-L+1)}{(R-L+1)*(R-L)}

只需要用莫队来维护 a^2 平方数即可。

网上的答案:就是把原来的挖出来-=num[x]*num[x],然后num[x]++,再填上去+=num[x]*num[x]

我的想法:每次添加和之前的比较:(a+1)=a^2+2a+1   ,添加时只要进行+=2a+1   /   -= (2a-1)即可

#include
using namespace std;
typedef long long ll;
const int N=1e5+10;
template 
void read(T &x){
    T f=1;char c=getchar();
    for( ; !isdigit(c) ; c=getchar()){ if(f=='-') f=-1; }
    for( ;  isdigit(c) ; c=getchar()){ x=x*10+c-'0';}
}
int n,m,a[N],num[N],unit;
ll tmp;
typedef struct node{
    int L,R,No;
    bool operator < (const node &p)const {
        return L/unit!=p.L/unit ? L/unit < p.L/unit : RQ[i].R) {  del(a[(R--)]); }
        while(LQ[i].L) {  add(a[(--L)]); }
        ans[Q[i].No]=F(i);
    }
    for(int i=1;i<=m;i++){
        printf("%lld/%lld\n",ans[i].F1,ans[i].F2);
    }
}
int main()
{
    read(n),read(m);
    for(int i=1;i<=n;i++){
        read(a[i]);
    }
    for(int i=1;i<=m;i++){
        read(Q[i].L),read(Q[i].R),Q[i].No=i;
    }
    solve();
    return 0;
}
/*
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
*/

自己的做法:

这个是我看着示例推出来的,因为每一次添加一种之前在序列中已有的颜色,

不就是对已有的颜色构建关系吗?那么我只要维护整个分子即可。

add( ): 加上序列中出现过的次数, 次数+1。

del ( ) :次数-1 ,减去序列中出现过的次数。

分母就是组合数即可。

#include
using namespace std;
typedef long long ll;
const int N=1e5+10;
template 
void read(T &x){
    T f=1;char c=getchar();
    for( ; !isdigit(c) ; c=getchar()){ if(f=='-') f=-1; }
    for( ;  isdigit(c) ; c=getchar()){ x=x*10+c-'0';}
}
int n,m,a[N],num[N],unit;
ll tmp;
typedef struct node{
    int L,R,No;
    bool operator < (const node &p)const {
        return L/unit!=p.L/unit ? L/unit < p.L/unit : RQ[i].R) {  del(a[(R--)]); }
        while(LQ[i].L) {  add(a[(--L)]); }
        ans[Q[i].No]=F(i);
    }
    for(int i=1;i<=m;i++){
        printf("%lld/%lld\n",ans[i].F1,ans[i].F2);
    }
}
int main()
{
    read(n),read(m);
    for(int i=1;i<=n;i++){
        read(a[i]);
    }
    for(int i=1;i<=m;i++){
        read(Q[i].L),read(Q[i].R),Q[i].No=i;
    }
    solve();
    return 0;
}
/*
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
*/

还有其他题:

牛客第八场 F

牛客第九场 F

牛客第十场 E

HDU 某一场的莫队

codeforces 375D Tree and Queries 题解 
codeforces 86D power array题解 
hdu 4638 Group 题解 

你可能感兴趣的:(莫队算法)