BZOJ 2038: 小Z的袜子

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2038


题目大意

给定一个序列。

每次询问一个区间,求在这个区间中选取两个数,两数相同的概率有多大。


算法:

这道题是莫队算法模板题。

莫队算法是一种解决区间询问类问题的通用算法,效率是O(n * sqrt(n)) 乘以每次询问的复杂度。

莫队算法有两种实现方法,一种是欧几里得最小生成树,一种是分块算法。

我采用的是分块算法,编码复杂度比较低。

 

先把询问按  sqrt(l)排序、分成sqrt(n)个块,然后每块中按照r排序

最后每次从前一个询问修改得到下一个询问的值。

总的时间复杂度是O(n * sqrt(n)) 乘以每次询问的复杂度,在此不再证明。


这道题我在去成都赛区的时候就做出来了,一个月过去了,一直忘了贴了。。

 

代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <climits>
#include <cmath>
#include <queue>
#include <vector>
#include <stack>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;

typedef long long LL;
#define mp make_pair
#define fi first
#define nd second
typedef pair <int, int> PII;
typedef pair <PII, int> PIII;

const int maxn = 51000, maxm = 51000;
PIII a[maxm];
long long ans1[maxm], ans2[maxm], cot[maxn];
int c[maxn];
int n, m;

LL gcd(LL a, LL b)
{
    return b ? gcd(b, a % b) : a;
}

void update(int pos, int x, int flg)
{
        ans1[pos] -= cot[c[x]] * cot[c[x]];
        cot[c[x]] += flg;
        ans1[pos] += cot[c[x]] * cot[c[x]];
}

bool cmp(const PIII  &a, const PIII &b)
{
    int posa = a.fi.fi/(int)sqrt(n);
    int posb = b.fi.fi/(int)sqrt(n);
    if(posa == posb)
    {
        return a.fi.nd < b.fi.nd;
    }
    return posa < posb;
}

int main()
{
    while(scanf("%d %d", &n, &m) == 2)
    {
        memset(cot, 0, sizeof(cot));
        for(int i = 1; i <= n; i ++)
        {
            scanf("%d", &c[i]);
        }
        for(int i = 0; i < m; i ++)
        {
            int l, r;
            scanf("%d %d", &l, &r);
            a[i] = mp(mp(l, r), i);
            ans2[i] = r - l + 1;
        }
        sort(a, a + m, cmp);
        for(int i = 0, l = 1, r = 0; i < m; i ++)
        {
            if(i)
            {
                ans1[a[i].nd] = ans1[a[i - 1].nd];
            }
            else
            {
                ans1[i] = 0;
            }
            while(l > a[i].fi.fi)
            {
                l --;
                update(a[i].nd, l, 1);
            }
            while(l < a[i].fi.fi)
            {
                update(a[i].nd, l, -1);
                l ++;
            }
            while(r > a[i].fi.nd)
            {
                update(a[i].nd, r, -1);
                r --;
            }
            while(r < a[i].fi.nd)
            {
                r ++;
                update(a[i].nd, r, 1);
            }
        }
        for(int i = 0; i < m; i ++)
        {
            ans1[i] -= ans2[i];
            ans2[i] *= ans2[i] - 1;
            if(ans1[i])
            {
                long long k = gcd(ans1[i], ans2[i]);
                ans1[i] /= k;
                ans2[i] /= k;
            }
            else
            {
                ans2[i] = 1;
            }
            printf("%lld/%lld\n",ans1[i], ans2[i]);
        }
    }
    return 0;
}


 

你可能感兴趣的:(BZOJ 2038: 小Z的袜子)