[Luogu P4867] [BZOJ 3809] Gty的二逼妹子序列

洛谷传送门
BZOJ传送门

题目描述

Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题。

对于一段妹子们,他们想让你帮忙求出这之内美丽度 ∈ [ a , b ] \in [a,b] [a,b]的妹子的美丽度的种类数。

为了方便,我们规定妹子们的美丽度全都在 [ 1 , n ] [1,n] [1,n]中。
给定一个长度为 n ( 1 ≤ n ≤ 100000 ) n(1 \le n \le 100000) n(1n100000)的正整数序列 s ( 1 ≤ s i ≤ n ) , 对 s(1 \le s_i \le n),对 s(1sin) m ( 1 ≤ m ≤ 1000000 ) m(1 \le m \le 1000000) m(1m1000000)次询问l,r,a,b,每次输出 s l ⋯ s r s_l \cdots s_r slsr中,权值 ∈ [ a , b ] \in [a,b] [a,b]的权值的种类数。

输入输出格式

输入格式:

第一行包括两个整数 n , m ( 1 ≤ n ≤ 100000 , 1 ≤ m ≤ 1000000 ) n,m(1 \le n \le 100000,1 \le m \le 1000000) n,m(1n100000,1m1000000),表示数列 s s s中的元素数和询问数。

第二行包括 n n n个整数 s 1 … s n ( 1 ≤ s i ≤ n ) s_1…s_n(1 \le s_i \le n) s1sn(1sin)

接下来 m m m行,每行包括 4 4 4个整数 l , r , a , b ( 1 ≤ l ≤ r ≤ n , 1 ≤ a ≤ b ≤ n ) l,r,a,b(1 \le l \le r \le n,1 \le a \le b \le n) l,r,a,b(1lrn,1abn),意义见题目描述。

保证涉及的所有数在C++的int内。保证输入合法。

输出格式:

对每个询问,单独输出一行,表示 s l ⋯ s r s_l \cdots s_r slsr中权值 ∈ [ a , b ] \in [a,b] [a,b]的权值的种类数。

输入输出样例

输入样例#1:
10 10
4 4 5 1 4 1 5 1 2 1
5 9 1 2
3 4 7 9
4 4 2 5
2 3 4 7
5 10 4 4
3 9 1 1
1 4 5 9
8 9 3 3
2 2 1 6
8 9 1 4
输出样例#1:
2
0
0
2
1
1
1
0
1
2

解题分析

神奇的操作。

显然用线段树配合莫队搞搞, 因为会加入/删除 m n m\sqrt n mn 次, 那么总复杂度是 O ( m n l o g ( n ) + m l o g ( n ) ) O(m\sqrt nlog(n)+mlog(n)) O(mn log(n)+mlog(n))的, 显然不是很能过。

考虑如何均摊这个复杂度。 前面那一坨太大了, 如果能把 l o g ( n ) log(n) log(n)改成 O ( 1 ) O(1) O(1)就好了。

那么显然我们可以对值域分块, 做到 O ( 1 ) O(1) O(1)插入块中, 然后 O ( n ) O(\sqrt n) O(n )遍历小块, O ( n ) O(\sqrt n) O(n )遍历大块做到。

这样总复杂度是 O ( m n + m n ) O(m\sqrt n+m\sqrt n) O(mn +mn ), 可以 A A A了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <climits>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1005000
#define MXX 100500
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
int seg[MXX], buf[MXX], val[MXX], id[MXX], ans[MX];
int blk[330][330], cnt[330];
int siz, n, m, dif;
struct Query {int lef, rig, lb, rb, id;} que[MX];
IN bool operator < (const Query &x, const Query &y)
{
    if (seg[x.lef] ^ seg[y.lef]) return seg[x.lef] < seg[y.lef];
    return (seg[x.lef] & 1) ? seg[x.rig] < seg[y.rig] : seg[x.rig] > seg[y.rig];
}
IN void insert(R int v)
{
    int bk = (v - 1) / siz + 1, pos = v - (bk - 1) * siz;
    if (!blk[bk][pos]) ++cnt[bk];
    blk[bk][pos]++;
}
IN void erase(R int v)
{
    int bk = (v - 1) / siz + 1, pos = v - (bk - 1) * siz;
    if (blk[bk][pos] == 1) --cnt[bk];
    blk[bk][pos]--;
}
IN int query(R int lb, R int rb)
{
    if (lb > rb) return 0; --lb;
    int bk = lb / siz, ret = 0, bd = lb - bk * siz, nex = bk + 1;
    for (R int i = 1; i <= bk; ++i) ret -= cnt[i];
    for (R int i = 1; i <= bd; ++i) ret -= (blk[nex][i] > 0);
    bk = rb / siz, bd = rb - bk * siz, nex = bk + 1;
    for (R int i = 1; i <= bk; ++i) ret += cnt[i];
    for (R int i = 1; i <= bd; ++i) ret += (blk[nex][i] > 0);
    return ret;
}
int main(void)
{
    in(n), in(m); int ql, qr, SIZ = std::sqrt(n);
    for (R int i = 1; i <= n; ++i) seg[i] = (i - 1) / SIZ + 1;
    for (R int i = 1; i <= n; ++i) in(val[i]), buf[i] = val[i];
    std::sort(buf + 1, buf + 1 + n);
    dif = std::unique(buf + 1, buf + 1 + n) - buf - 1;
    siz = std::sqrt(dif);
    for (R int i = 1; i <= n; ++i) id[i] = std::lower_bound(buf + 1, buf + dif + 1, val[i]) - buf;
    for (R int i = 1; i <= m; ++i)
    in(que[i].lef), in(que[i].rig), in(que[i].lb), in(que[i].rb), que[i].id = i;
    std::sort(que + 1, que + 1 + m);
    R int l = que[1].lef, r = que[1].rig;
    for (R int i = que[1].lef; i <= que[1].rig; ++i) insert(id[i]);
    ql = std::lower_bound(buf + 1, buf + 1 + dif, que[1].lb) - buf;
    qr = std::lower_bound(buf + 1, buf + 1 + dif, que[1].rb) - buf;
    if (ql > dif) goto jp;
    if (qr > dif) --qr;
    if (buf[qr] > que[1].rb) --qr;
    ans[que[1].id] = query(ql, qr);
    jp :;
    for (R int i = 2; i <= m; ++i)
    {
        W (l > que[i].lef) insert(id[--l]);
        W (l < que[i].lef) erase(id[l++]);
        W (r > que[i].rig) erase(id[r--]);
        W (r < que[i].rig) insert(id[++r]);
        ql = std::lower_bound(buf + 1, buf + 1 + dif, que[i].lb) - buf;
        qr = std::lower_bound(buf + 1, buf + 1 + dif, que[i].rb) - buf;
        if (ql > dif) continue;
        if (qr > dif) --qr;
        if (buf[qr] > que[i].rb) --qr;
        ans[que[i].id] = query(ql, qr);
    }
    for (R int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值