其他分享
首页 > 其他分享> > HYSBZ - 2038【K-th Number】 莫队入门

HYSBZ - 2038【K-th Number】 莫队入门

作者:互联网

HYSBZ - 2038【K-th Number】 莫队入门

https://cn.vjudge.net/contest/304248#problem/A

题意

给定 n 个数和 m 次询问,其中 n 个数表示的是“袜子的颜色”,每次询问输出抽到相同颜色袜子的概率(最简分数)。

分析

做主席树做到一题树上莫队,然后懵了,于是回来从普通莫队算法开始补起好了。

莫队算法:基本可以分为三类,普通莫队、树形莫队、带修改莫队。

思路:离线情况下对所有的询问进行一个美妙的 sort(),然后用两个指针l, r(本题是两个,其他的题可能会更多)不断以看似暴力的方式在区间内跳来跳去,最终输出答案。当知道一个区间的信息后,要求出旁边区间的信息(旁边区间由当前区间的一个指针通过 +1/-1 得到),只需要 \(O(1)\) 的时间。

那么什么才是美妙的 sort() 呢?如果以读入的顺序来枚举每个询问,那么可能会出现指针 l、r 跳来跳去的景象,这样的复杂度与暴力并无二致 \(O(n^2)\) 。而莫队算法采用的是分块的思想,对于两个不同查询来说,如果它们的左端点 l 在同一块,那么就按照右端点 r 进行排序;否则还是正常按左端点 l 排序。如果这样做,这样就会得到优化后的时间复杂度:\[O(n*block+n*n/block)\]

即:\[O(n*\sqrt{n})\]

值得一提的是,莫队算法的核心和难点在与判断相邻两块可以通过什么样的数学关系进行关联(如何转移)。还有就是先动指针还是先转移千万不能弄乱了。

对于这题来说,分母是 \(n*n-n\) (表示两两袜子之间的随机组合),分子是 \(res-n\),其中 \(res\) = “该区间内每种颜色 i 出现次数 sum[i] 的平方”。

代码

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <numeric>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

const int maxn = 5e4+5;

int n, m;
int a[maxn];
int block;          // 块数
ll mark[maxn];     // 标记当前区间内颜色 i 已经出现 mark[i] 次
ll res;            // 当前答案
struct Question {
    int l, r, id;
    bool operator < (const Question &a) const {
        if(l/block == a.l/block) {  // 只有当左端点在同一块中,才按照右端点排序
            return r < a.r;
        }
        return l < a.l;
    }
}Q[maxn];

struct ANS {
    ll a, b;
}ans[maxn];

void init() {
    block = sqrt(n);
    memset(mark, 0, sizeof(mark));
    res = 0;
}

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a%b);
}

void del(int x) {
    res -= mark[a[x]]*mark[a[x]];
    mark[a[x]] --;
    res += mark[a[x]]*mark[a[x]];
}

void add(int x) {
    res -= mark[a[x]]*mark[a[x]];
    mark[a[x]] ++;
    res += mark[a[x]]*mark[a[x]];
}

int main() {
    while(~scanf("%d%d", &n, &m)) {
        init();
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
        }
        for(int i = 1; i <= m; i++) {
            scanf("%d%d", &Q[i].l, &Q[i].r);
            Q[i].id = i;
        }
        sort(Q+1, Q+1+m);
        int l = 1, r = 0;
        for(int i = 1; i <= m; i++) {
            while(l < Q[i].l) {
                del(l);  // 左端当前点为多余点,先删再移。
                l++;
            }
            while(l > Q[i].l) {
                l--;
                add(l);  // 左端有节点未加入,先移再加。
            }
            while(r < Q[i].r) {
                r++;
                add(r);  // 右端有节点未加入,先移再加。
            }
            while(r > Q[i].r) {
                del(r);  // 右端当前点为多余点,先删再移。
                r--;
            }
            ans[Q[i].id].a = res - (r-l+1);
            ans[Q[i].id].b = 1ll*(r-l+1)*(r-l);
        }
        for(int i = 1; i <= m; i++) {
            ll t = gcd(ans[i].a, ans[i].b);
            if(ans[i].a == 0) {
                printf("0/1\n");
            }
            else
                printf("%lld/%lld\n", ans[i].a/t, ans[i].b/t);
        }
    }
    return 0;
}

标签:const,int,HYSBZ,Number,mark,th,res,include,莫队
来源: https://www.cnblogs.com/Decray/p/10932680.html