BZOJ 2038: 小Z的袜子

来源:互联网 发布:网络硬盘 免费 编辑:程序博客网 时间:2024/04/19 16:42

题目链接: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-8using namespace std;typedef long long LL;#define mp make_pair#define fi first#define nd secondtypedef 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;}


 

原创粉丝点击