首页 > 其他 > 详细

特辑:莫队

时间:2019-07-24 16:51:02      阅读:86      评论:0      收藏:0      [点我收藏+]

题目按难易度为关键字升序排列。

Problem L:小B的询问

莫队板子题,秒了

#include <bits/stdc++.h>

int n, m, k, a[50005], pos[50005], cnt[50005];
long long ans[50005], tmp;

inline int R() {
    int a = 0; char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
    return a;
}

struct Block {
    int l, r, id;
    friend bool operator <(Block a, Block b) {
        if (pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l];
        if (pos[a.l] & 1) return a.r < b.r;
        else return a.r > b.r;
    }
} ask[50005];

inline void Add(int x) {
    tmp += 2 * cnt[a[x]] + 1, ++cnt[a[x]];
}

inline void Del(int x) {
    tmp += -2 * cnt[a[x]] + 1, --cnt[a[x]];
}

signed main() {
    n = R(), m = R(), k = R();
    for (int i = 1; i <= n; i++) a[i] = R();
    int blk = pow(n, 0.55);
    for (int i = 1; i <= n; i++)
        pos[i] = (i - 1) / blk + 1;
    for (int i = 1; i <= m; i++) ask[i].l = R(), ask[i].r = R(), ask[i].id = i;
    std::sort(ask + 1, ask + 1 + m);
    for (int i = 1, l = 1, r = 0; i <= m; i++) {
        while (l > ask[i].l) Add(--l);
        while (l < ask[i].l) Del(l++);
        while (r < ask[i].r) Add(++r);
        while (r > ask[i].r) Del(r--);
        ans[ask[i].id] = tmp;
    }
    for (int i = 1; i <= m; i++)
        printf("%lld\n", ans[i]);
    return 0;
}

Problem M: 小Z的袜子

分母部分为选两只袜子总方案数 $ C_{R-L+1} ^ 2 $.

分子部分为每种颜色袜子选两次方案数 \(\sum C_{cnt_i}^2\).

所以答案为 \(\frac{\sum C_{cnt_i}^2}{C_{R-L+1} ^ 2} = \frac{\sum cnt_i^2 - \sum cnt_i}{(R-L+1)(R-L)}\)

转移显然了,懒得写

Add:

#include <bits/stdc++.h>
#define ll long long

const int N = 50000 + 233;
int n, m;
ll pos[N], block, cnt[N], ans[N], c[N], above[N], below[N], tmp;
bool zero[N];

struct Node {
    int l, r, id;
    friend bool operator <(Node a, Node b) {
        if (pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l];
        else if (pos[a.l] & 1) return a.r < b.r;
        else return a.r > b.r;
    }
} nd[N];

inline ll R() {
    int a = 0; char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
    return a;
}

ll gcd(ll x, ll y) {
    return y == 0 ? x : gcd(y, x % y);
}

inline void Add(ll x) {
    tmp += 2 * cnt[c[x]] + 1, ++cnt[c[x]];
}

inline void Del(ll x) {
    tmp -= 2 * cnt[c[x]] - 1, --cnt[c[x]];
}

void print(ll x, ll y) {
    x -= y, y = y * (y - 1);
    ll g = gcd(x, y);
    printf("%lld/%lld\n", x / g, y / g);
}

signed main() {
    n = R(), m = R();
    for (int i = 1; i <= n; i++) 
        c[i] = R();
    for (int i = 1; i <= m; i++)
        nd[i].l = R(), nd[i].r = R(), nd[i].id = i;
    block = pow(n, 0.55);
    for (int i = 1; i <= n; i++)
        pos[i] = (i - 1) / block + 1;
    std::sort(nd + 1, nd + 1 + m);
    for (int i = 1, l = 1, r = 0; i <= m; i++) {
        int L = nd[i].l, R = nd[i].r, id = nd[i].id;
        if (L == R) {
            zero[id] = 1;
            continue;
        }
        while (l > L) Add(--l);
        while (l < L) Del(l++);
        while (r < R) Add(++r);
        while (r > R) Del(r--);
        above[id] = tmp, below[nd[i].id] = R - L + 1;
    }
    for (int i = 1; i <= m; i++) {
        if (zero[i]) puts("0/1");
        else print(above[i], below[i]);
    }
    return 0;
}

Problem K: 作业

直接莫队把每种数的出现次数和种类维护了。

难点在统计,很多人写了树状数组,其实复杂度多了个log,是不对的,放在时限小的OJ(比如Luogu)就直接凉了

正解应该对值域分块,\(O(N \sqrt N)\)解决。

如果你会CDQ分治那不是更好吗

数组开小WA了4次,丢人(

#include <bits/stdc++.h>

const int N = 2000000 + 233;
int n, m, a[N], block, pos[N], cnt[N], ans_1[N], ans_2[N], L[N], f[N], g[N];

struct Node {
    int l, r, a, b, id;
    friend bool operator <(Node a, Node b) {
        if (pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l];
        else if (pos[a.l] & 1) return a.r < b.r;
        else return a.r > b.r;
    } 
} nd[N];

inline int R() {
    int a = 0; char c = getchar();
    while (!isdigit(c)) c = getchar();
    while (isdigit(c)) a = a * 10 + c - '0', c = getchar();
    return a;
}

inline void Add(int x) {
    f[pos[x]]++;
    if (++cnt[x] == 1) g[pos[x]]++;
}

inline void Del(int x) {
    f[pos[x]]--;
    if (--cnt[x] == 0) g[pos[x]]--;
}

inline void Ask(int x) {
    int l = nd[x].a, r = nd[x].b, id = nd[x].id;
    if (pos[r] - pos[l] < 2) {
        for (int i = l; i <= r; i++)
            if (cnt[i]) ans_1[id] += cnt[i], ans_2[id]++;
        return;
    }
    for (int i = l; i < L[pos[l] + 1]; i++)
        if (cnt[i]) ans_1[id] += cnt[i], ans_2[id]++;
    for (int i = L[pos[r]]; i <= r; i++)
        if (cnt[i]) ans_1[id] += cnt[i], ans_2[id]++;
    for (int i = pos[l] + 1; i < pos[r]; i++)
        ans_1[id] += f[i], ans_2[id] += g[i];
}

signed main() {
    n = R(), m = R();
    for (int i = 1; i <= n; i++) 
        a[i] = R();
    for (int i = 1; i <= m; i++)
        nd[i].l = R(), nd[i].r = R(), nd[i].a = R(), nd[i].b = R(), nd[i].id = i;
    block = sqrt(n);
    for (int i = 1; i <= n; i++) {
        pos[i] = (i - 1) / block + 1;
        if (pos[i] != pos[i - 1]) L[pos[i]] = i;
    }
    L[pos[n + 1] = pos[n] + 1] = n + 1;
    std::sort(nd + 1, nd + 1 + m);
    for (int i = 1, l = 1, r = 0; i <= m; i++) {
        while (l < nd[i].l) Del(a[l++]);
        while (l > nd[i].l) Add(a[--l]);
        while (r < nd[i].r) Add(a[++r]);
        while (r > nd[i].r) Del(a[r--]);
        Ask(i);
    }
    for (int i = 1; i <= m; i++)
        printf("%d %d\n", ans_1[i], ans_2[i]);
    return 0;
}

Problem N: Permu

不会,咕着

特辑:莫队

原文:https://www.cnblogs.com/gekoo/p/11238961.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!