第十三届蓝桥杯大赛软件赛省赛(C++研究生组)

A: 裁纸刀

可以证明,只要首先裁剪最外围的 4 4 4 条边,之后无论怎样裁剪,次数都是最少。对于 n n n m m m 列的二维码,至少需要裁剪 n m + 3 nm + 3 nm+3 次,因此答案为 20 × 22 + 3 = 443 20\times 22+3=443 20×22+3=443

证明:只需证明裁掉边界后至少还需裁剪 n m − 1 nm-1 nm1 次。

  • n = 1 , m ≥ 1 n=1,m\ge 1 n=1,m1 时至少还需裁剪 m − 1 m-1 m1 次,结论显然成立。
  • n > 1 , m = 1 n > 1, m=1 n>1,m=1 时至少还需裁剪 n − 1 n-1 n1 次,结论显然成立。
  • 若对于所有的 n ≤ i , m ≤ j n \le i, m\le j ni,mj ( n , m ) ≠ ( i , j ) (n,m)\ne (i,j) (n,m)=(i,j) 时结论均成立,当 ( n , m ) = ( i , j ) (n,m)=(i,j) (n,m)=(i,j)(其中 i , j > 1 i,j > 1 i,j>1) 时,
    • 若后续第一刀裁去第 r r r 行的下边界( 1 ≤ r ≤ n − 1 1 \le r \le n - 1 1rn1),则至少还需裁剪 d 1 = 1 + [ r m − 1 ] + [ ( n − r ) m − 1 ] = n m − 1 d_1=1+[rm-1]+[(n-r)m-1]=nm-1 d1=1+[rm1]+[(nr)m1]=nm1
    • 若后续第一刀裁去第 c c c 列的右边界( 1 ≤ c ≤ m − 1 1 \le c \le m - 1 1cm1),则至少还需裁剪 d 2 = 1 + [ n c − 1 ] + [ n ( m − c ) − 1 ] = n m − 1 d_2=1+[nc-1]+[n(m-c)-1]=nm-1 d2=1+[nc1]+[n(mc)1]=nm1
    • d 1 = d 2 = n m − 1 d_1=d_2=nm-1 d1=d2=nm1 n > 1 , m > 1 n > 1,m > 1 n>1,m>1 时结论成立。

B: 灭鼠先锋

将棋盘的格子按行优先的顺序从 1 1 1 8 8 8 编号,每一个格子有“已放置棋子”和“未放置棋子”两种状态。因此棋盘的状态可用 8 8 8 个二进制位表示。用程序模拟下棋过程即可,答案:LLLV。

#include <iostream>
using namespace std;

const int MASK = (1 << 8) - 1;

// 棋盘状态为 status 时下一步操作者是否可以获胜?
bool dfs(int status) {
    if (status == MASK) {
        return true;
    }
    for (int i = 0; i < 8; ++i) {  // 在一个空位上放置一个棋子
        if (status & (1 << i)) continue;
        if (!dfs(status | (1 << i))) return true;
    }
    for (int i = 0; i < 3; ++i) {  // 在同一行的连续两个空位上各放置一个棋子
        if (!(status & (3 << i))) {
            if (!dfs(status | (3 << i))) return true;
        }
        if (!(status & (3 << i + 4))) {
            if (!dfs(status | (3 << i + 4))) return true;
        }
    }
//    for (int i = 0; i < 4; ++i) {  // 同一列的连续两个空位上各放置一个棋子,不合题意
//        if (status & (17 << i)) continue;
//        if (!dfs(status | (17 << i))) return true;
//    }
    return false;
}

int main() {
    printf(dfs(1) ? "L" : "V");
    printf(dfs(3) ? "L" : "V");
    printf(dfs(2) ? "L" : "V");
    printf(dfs(6) ? "L" : "V");
    return 0;
}

C: 质因数个数

对于正整数 n = p 1 a 1 p 2 a 2 ⋯ p k a k n=p_1^{a_1}p_2^{a_2}\cdots p_k^{a_k} n=p1a1p2a2pkak ,其中 p i p_i pi 为质数且 p 1 < p 2 < ⋯ < p k p_1 < p_2 < \cdots < p_k p1<p2<<pk .

首先找到最小素因子 p 1 p_1 p1 并剔出所有 p 1 p_1 p1 ;然后找到最小素因子 p 2 p_2 p2 并剔出所有 p 2 p_2 p2 ;依此类推.

时间复杂度 O ( n ) O(\sqrt{n}) O(n ) ,空间复杂度 O ( 1 ) O(1) O(1) .

#include <iostream>
using namespace std;

int main() {
    long long n;
    cin >> n;
    int cnt = 0;
    for (int i = 2; i <= n / i; ++i) {
        if (n % i == 0) ++cnt;
        while (n % i == 0) n /= i;
    }
    if (n > 1) ++cnt;
    cout << cnt;
    return 0;
}

D: 选数异或

区间中存在两个数的异或等于 x ,等价于区间中同时存在 tt^x .

d p r dp_r dpr 记录以 r r r 为右端点的“满足要求的短区间”的左端点。也就是说计算 d p r dp_r dpr 使得区间 [ d p r , r ] [dp_r,r] [dpr,r] 满足要求,但 [ d p r + 1 , r ] [dp_r+1,r] [dpr+1,r] 不满足要求。

对于每一个查询 l i , r i l_i,r_i li,ri ,只需判断 l i ≤ d p r i l_i \le dp_{r_i} lidpri 是否成立即可。时间复杂度 O ( n ) O(n) O(n) .

当然,本题还可以采用莫队算法解决(这不是本题的正解),下面仅给出莫队算法的实现。

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std;

struct node {
    int l, r, i;
    bool operator< (const node &t) {
        if (r != t.r) return r < t.r;
        return l < t.l;
    }
};

int main() {
    int n, m, x;
    scanf("%d%d%d", &n, &m, &x);
    vector<int> a(n);
    for (auto &p : a) scanf("%d", &p);
    vector<node> q(m);
    {
        int i = 0;
        for (auto &t : q) {
            scanf("%d%d", &t.l, &t.r);
            t.i = i++;
            --t.l;
            --t.r;
        }
    }
    sort(q.begin(), q.end());
    vector<bool> ans(m);
    int step = max(sqrt(n), 1.);
    for (int i = 0; i < n; i += step) {
        int cnt = 0;
        vector<int> st(1 << 20);
        int L = 0, R = -1;
        for (auto &t : q) {
            if (t.l < i || t.l >= i + step) continue;
            if (R == -1) L = t.l, R = L - 1;
            while (R < t.r) {
                int y = a[++R];
                ++st[y];
                if (x == 0 && st[y] == 2) ++cnt;
                else if (x && st[y^x] && st[y] == 1) ++cnt;
            }
            while (L < t.l) {
                int y = a[L++];
                --st[y];
                if (x == 0 && st[y] == 1) --cnt;
                else if (x && st[y^x] && st[y] == 0) --cnt;
            }
            while (L > t.l) {
                int y = a[--L];
                ++st[y];
                if (x == 0 && st[y] == 2) ++cnt;
                else if (x && st[y^x] && st[y] == 1) ++cnt;
            }
            ans[t.i] = (cnt > 0);
        }
    }
    for (int i = 0; i < m; ++i) {
        if (ans[i]) puts("yes");
        else puts("no");
    }
    return 0;
}

做真题的好处在于,可以让自己意识到在这个位置的题目不应该离谱到需要莫队来解。

E: GCD

因为 g c d ( a + k , b + k ) = g c d ( ∣ a − b ∣ , b + k ) gcd(a+k,b+k)=gcd(\left|a-b\right|,b+k) gcd(a+k,b+k)=gcd(ab,b+k) ,因此问题转化为寻找最小的正整数 k k k 使得 b + k b+k b+k ∣ a − b ∣ |a-b| ab 的倍数。

时空复杂度均为 O ( 1 ) O(1) O(1) .

#include <iostream>
using namespace std;

int main() {
    long long a, b, k;
    cin >> a >> b;
    a = abs(a - b);
    a += b / a * a;
    k = a - b;
    cout << k;
    return 0;
}

F: 爬树的甲壳虫

设随机变量 X i X_i Xi 代表甲壳虫从树根爬到高度 i i i 需要经过的时间( 1 ≤ i ≤ n 1 \le i \le n 1in).

随机变量 X 1 X_1 X1 的期望为

E ( X 1 ) = ∑ k = 0 ∞ ( k + 1 ) ⋅ P i k ( 1 − P i ) = 1 1 − P i , E(X_1)=\sum\limits_{k=0}^{\infty}(k+1)\cdot P_i^k(1-P_i)=\dfrac{1}{1-P_i}, E(X1)=k=0(k+1)Pik(1Pi)=1Pi1,

随机变量 X i X_i Xi i > 1 i > 1 i>1)的期望为

E ( X i ) = ∑ k = 0 ∞ ( k + 1 ) ( E ( X i − 1 ) + 1 ) ⋅ P i k ( 1 − P i ) = E ( X i − 1 ) + 1 1 − P i . E(X_i)=\sum\limits_{k=0}^{\infty}(k+1)(E(X_{i-1})+1)\cdot P_i^k(1-P_i)=\dfrac{E(X_{i-1})+1}{1-P_i}. E(Xi)=k=0(k+1)(E(Xi1)+1)Pik(1Pi)=1PiE(Xi1)+1.

因为 P = 998244353 P=998244353 P=998244353 是一个质数,由费马小定理知, ∀ a ( gcd ⁡ ( a , P ) = 1 ) \forall a(\gcd(a,P)=1) a(gcd(a,P)=1) ,可求得 a a a 的乘法逆元为 a p − 2 a^{p-2} ap2 .

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn) ,空间复杂度 O ( 1 ) O(1) O(1) .

#include <iostream>
using namespace std;

long long qpow(long long a, int b, int mod) {
    a %= mod;
    long long t = 1 % mod;
    while (b) {
        if (b & 1) t = t * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return t;
}

int inv(int a, int p) {
    return qpow(a, p - 2, p);
}

int main() {
    int n;
    scanf("%d", &n);
    int ex = 0;
    const int p = 998244353;
    for (int i = 1; i <= n; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        x = y - x;
        ex = (ex + 1LL) * y % p * inv(x, p) % p;
    }
    printf("%d\n", ex);
    return 0;
}

值得一提的是,当 y − x = P y-x=P yx=P 时本题无解,比如这组样例:

1

1 998244354

提交本题代码的时候发现使用 INT_MAX 需要 #include<climits> ,否则会出现编译错误。

G: 全排列的价值

对于一个排列 A = ( a 1 , a 2 , ⋯   , a n ) A=(a_1,a_2,\cdots ,a_n) A=(a1,a2,,an) ,必存在一个与之对应的 B = ( b 1 , b 2 , ⋯   , b n B=(b_1,b_2,\cdots ,b_n B=(b1,b2,,bn) ,其中 b i = n + 1 − a i b_i=n+1-a_i bi=n+1ai ,使得 c i = ∣ { a j ∣ j < i , a j < a i } ∣ = ∣ { b j ∣ j < i , b j > b i } ∣ c_i=\left|\{a_j|j < i,a_j < a_i\}\right|=\left|\{b_j|j < i,b_j > b_i\}\right| ci={ajj<i,aj<ai}={bjj<i,bj>bi} .

所有排列的价值之和为 1 2 ⋅ n ! ⋅ C n 2 \dfrac 12\cdot n!\cdot \mathrm C_n^2 21n!Cn2

#include <iostream>
using namespace std;

int main() {
    int n;
    scanf("%d", &n);
    const int p = 998244353;
    int ans = 1LL * n * (n - 1) / 2 % p;
    for (int i = 3; i <= n; ++i) {
        ans = 1LL * ans * i % p;
    }
    printf("%d", ans);
    return 0;
}

H: 扫描游戏

由于棒从 y y y 轴正向开始顺时针扫描,因此首先将所有点按照顺时针排序。对于时针同时扫过的点,距原点近的点在前。

将排好序后的数组视为一个循环数组。让棒从初始位置出发,每次向后寻找第一个可以被棒扫到(距原点距离不超过棒长)的点。最多寻找 n n n 次之后算法结束。

算法复杂度取决于每次向后寻找下一个点的时间,如果顺序查找,总时间复杂度 O ( n 2 ) O(n^2) O(n2) . 使用线段树维护区间最小值之后可以二分找到下一个点,可使复杂度降为 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n) .

#include <iostream>
#include <vector>
#include <cmath>
#include <climits>
#include <algorithm>
using namespace std;

const long long INF = LLONG_MAX;

int gcd(int a, int b) {
    while (b) {
        int t = a % b;
        a = b;
        b = t;
    }
    return a;
}

struct A {
    int x, y;
    int dx, dy;  // (dx,dy) 代表向量 OA 的方向
    int z, i;
    long long l2;  // L2 范数
    double cs;  // OA 向量与 y 轴正向夹角的 cos 值
    A(int x, int y, int z, int i) : x(x), y(y), l2(1LL * x * x + 1LL * y * y), z(z), i(i) {
        if (x == 0 && y == 0) {
            dx = 0, dy = 1;
            cs = 1;
            return;
        }
        int d = abs(gcd(x, y));
        dx = x / d, dy = y / d;
        cs = dy / sqrt(1. * dx * dx + 1. * dy * dy);
    }
    bool operator< (const A &t) const {
        if (dx == t.dx && dy == t.dy) return l2 < t.l2;
        if (dx >= 0) {
            return t.dx < 0 || cs > t.cs;
        }
        else {
            return t.dx < 0 && cs < t.cs;
        }
    }
};

class SegTree {
    int n;
    vector<long long> tree;

public:
    void update(int p, long long x, int L, int R, int id) {
        if (L == R) {
            tree[id] = x;
            return;
        }

        int m = L + R >> 1;
        if (p <= m) update(p, x, L, m, id << 1);
        else update(p, x, m + 1, R, id << 1 | 1);
        tree[id] = min(tree[id << 1], tree[id << 1 | 1]);
    }

    SegTree(int n) : n(n) {
        tree.resize(n << 2, INF);
    }
    // 查询 [x, y] 内的最小值
    long long query(int x, int y, int L, int R, int id) {
        if (x <= L && R <= y) return tree[id];
        int m = L + R >> 1;
        long long mi = INF;
        if (x <= m) mi = min(mi, query(x, y, L, m, id << 1));
        if (y > m) mi = min(mi, query(x, y, m + 1, R, id << 1 | 1));
        return mi;
    }
    // 查询 [x, y] 内第一个小于等于 val 的位置,若不存在,返回 -1
    int query(int x, int y, long long val) {
        if (x > y || query(x, y, 1, n, 1) > val) return -1;
        int L = x, R = y;
        while (L < R) {
            int m = L + R >> 1;
            if (query(x, m, 1, n, 1) <= val) R = m;
            else L = m + 1;
        }
        return L;
    }
};

int main()
{
    // 输入
    int n;
    long long L;
    scanf("%d%lld", &n, &L);
    vector<A> a;

    for (int i = 0; i < n; ++i) {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        a.emplace_back(x, y, z, i);
    }

    sort(a.begin(), a.end());

//    for (auto &t : a) cout << t.x << ' ' << t.y << endl;

    SegTree st(n);

    vector<int> ans(n, -1);

    int last = 0;

    int r = 0;

    // 线段树初始化
    {
        int i = 0;
        for (auto &t : a) {
            st.update(++i, t.l2, 1, n, 1);
        }
    }


    // 检查向量 Ox Oy 是否同向
    auto check = [&] (A x, A y) -> bool {
        return x.dx == y.dx && x.dy == y.dy;
    };

    // 将数组 A 看作一个循环数组,每次寻找区间 [last + 1, n] U [1, last - 1] 区间内第一个可以扫到的点(即第一个 l2 范数小于等于 L^2 的点)
    while (true) {
        int x = st.query(last + 1, n, 1LL * L * L);
        if (x == -1) {
            x = st.query(1, last - 1, 1LL * L * L);
            if (x == -1) break;
        }
        if (last && check(a[last - 1], a[x - 1])) ans[a[x - 1].i] = ans[a[last - 1].i], ++r;
        else ans[a[x - 1].i] = ++r;
        L += a[x - 1].z;
        if (L > 2e9) L = 2e9;
        st.update(x, INF, 1, n, 1);
        last = x;
    }

    for (auto x : ans) printf("%d ", x);

    return 0;
}

I: 数的拆分

对于正整数 n = p 1 k 1 p 2 k 2 ⋯ p r k r n=p_1^{k_1}p_2^{k_2}\cdots p_r^{k_r} n=p1k1p2k2prkr ,其中 k i ∈ N + k_i \in \mathbb N^+ kiN+ p i p_i pi 为质数 且 p 1 < p 2 < ⋯ < p r p_1 < p_2 < \cdots < p_r p1<p2<<pr .

可以证明, n n n 能表示成 x 1 y 1 ⋅ x 2 y 2 x_1^{y_1}\cdot x_2^{y_2} x1y1x2y2 x 1 , x 2 , y 1 , y 2 ∈ N + x_1,x_2,y_1,y_2\in \mathbb N^+ x1,x2,y1,y2N+ y 1 , y 2 ≥ 2 y_1,y_2 \ge 2 y1,y22)的形式当且仅当 ∀ i ∈ { 1 , 2 , ⋯   , r } , k i ≥ 2 \forall i\in\{1,2,\cdots ,r\},k_i \ge 2 i{1,2,,r},ki2 .

必要性显然成立,下面证明充分性。若 ∀ i ∈ { 1 , 2 , ⋯   , r } , k i ≥ 2 \forall i\in\{1,2,\cdots ,r\},k_i \ge 2 i{1,2,,r},ki2,记 l i = { 1 , k i 为奇数 0 , k i 为偶数 , m i = k i − 3 l i 2 l_i=\begin{cases}1,&k_i \text{为奇数}\\ 0,&k_i为偶数\end{cases},m_i=\dfrac{k_i-3l_i}{2} li={1,0,ki为奇数ki为偶数,mi=2ki3li . 则 n = ∏ i = 1 r p i k i = ∏ i = 1 r p i 2 m i + 3 l i = ( ∏ i = 1 r p i m i ) 2 ( ∏ i = 1 r p i l i ) 3 n=\prod\limits_{i=1}^rp_i^{k_i}=\prod\limits_{i=1}^rp_i^{2m_i+3l_i}=\left(\prod\limits_{i=1}^rp_i^{m_i}\right)^2 \left(\prod\limits_{i=1}^rp_i^{l_i}\right)^3 n=i=1rpiki=i=1rpi2mi+3li=(i=1rpimi)2(i=1rpili)3 . 证毕.

可见,只要所有素因子 p i p_i pi 的个数 k i ≥ 2 k_i \ge 2 ki2 n n n 一定可以表示成 x 1 2 ⋅ x 2 3 x_1^2\cdot x_2^3 x12x23 的形式.

n n n 可以表示成 x 1 2 ⋅ x 2 3 x_1^2\cdot x_2^3 x12x23 的形式,由题设知 x 1 2 ⋅ x 2 3 ≤ 1 0 18 x_1^2\cdot x_2^3\le 10^{18} x12x231018 ⇒ \Rightarrow min ⁡ ( x 1 , x 2 ) ≤ 1 0 18 5 = 1 0 3.6 < 4000 \min(x_1,x_2)\le\sqrt[5]{10^{18}}=10^{3.6}\lt 4000 min(x1,x2)51018 =103.6<4000 . 不难发现,如果除掉 n n n 中所有 4000 4000 4000 以内的素因子,剩下的部分一定是一个平方数或者立方数。

至此算法思路已然明了:

  • 预处理算出 4000 4000 4000 以内的所有质数,共 550 个 .
  • 对每一个正整数 e e e ,除掉其 4000 4000 4000 以内的所有素因子. 若除的过程中发现某个素因子仅出现一次,则该数无法拆分.
  • e e e 剩下的部分不是平方数或者立方数,则该数无法拆分.

时间复杂度 O ( ( 550 + ε ) T ) O((550+\varepsilon)T) O((550+ε)T) ,其中 ε \varepsilon ε 是计算平方根和立方根的时间.

#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

/**
 * Get all prime numbers between 1 and n, inclusively.
 */
void getPrimes(std::vector<int> &primes, int n) {
    std::vector<bool> vis(n + 1);
    for (int i = 2; i <= n; ++i) {
        if (!vis[i]) primes.push_back(i);
        for (int p : primes) {
            if (p > n / i) break;
            vis[p * i] = true;
            if (i % p == 0) break;
        }
    }
};

inline bool is_square(long long e) {
    long long x = round(sqrt(e));
    return x * x == e;
}

inline bool is_cubic(long long e) {
    long long x = round(pow(e, 1. / 3));
    return x * x * x == e;
}

int main()
{
    vector<int> primes;
    getPrimes(primes, 4e3);
    int T;
    scanf("%d", &T);
    vector<long long> a(T);
    for (auto &p : a) scanf("%lld", &p);

    for (auto &e : a) {
        bool ok = true;
        for (auto &p : primes) {
            if (e % p) continue;
            e /= p;
            if (e % p) {ok = false; break;}
            do e /= p; while (e % p == 0);
        }
        ok &= is_square(e) || is_cubic(e);
        puts(ok ? "yes" : "no");
    }

    return 0;
}

个人认为这是本场思维难度最大的一题,一开始真的毫无头绪。

J: 重复的数

  • 如果只有一次查询 ,可以用 O ( n ) O(n) O(n) 的时间得到答案。对于 m m m 次查询,可以用 O ( n m ) O(nm) O(nm) 的时间得到全部答案。
  • 维护一个动态区间,并用 c n t k cnt_k cntk 记录区间内恰好出现 k k k 次的数的个数, n u m x num_x numx 记录区间内数字 x x x 出现的次数。如果已知区间 [ l , r ] [l,r] [l,r] 的答案,则可以用 O ( 1 ) O(1) O(1) 的时间得到区间 [ l − 1 , r ] , [ l + 1 , r ] , [ l , r − 1 ] , [ l , r + 1 ] [l-1,r],[l+1,r],[l,r-1],[l,r+1] [l1,r],[l+1,r],[l,r1],[l,r+1] 的答案,因此可以使用莫队算法解决,时间复杂度 O ( n ⋅ m s t e p + n ⋅ s t e p ) O(\dfrac{n\cdot m}{step}+n\cdot step) O(stepnm+nstep) ,其中 s t e p step step 代表分块区间长度,当 s t e p step step m \sqrt{m} m 时理论时间复杂度可降至 O ( n m ) O(n\sqrt m) O(nm ) .

下方代码实现的 s t e p step step n \sqrt n n ,总的时间复杂度为 O ( n log ⁡ n + ( m + n ) n ) O(n\log n+(m+n)\sqrt n) O(nlogn+(m+n)n ) .

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;

struct node {
    int l, r, k, i;
    bool operator< (const node &t) const {
        return r < t.r;
    }
};
int main() {
    int n;
    scanf("%d", &n);
    vector<int> a(n);
    for (auto &p : a) scanf("%d", &p);
    int m;
    scanf("%d", &m);
    vector<node> v(m);
    {
        int i = 0;
        for (auto &t : v) {
            scanf("%d%d%d", &t.l, &t.r, &t.k);
            --t.l, --t.r;
            t.i = i++;
        }
    }
    sort(v.begin(), v.end());
    vector<int> cnt(n + 1), num(1e5 + 1), ans(m);
    int L = v[0].l, R = L - 1;
    int step = sqrt(n) + 1;
    bool rev = true;
    for (int i = 0; i < n; i += step) {
        rev ^= 1;
        for (int j = 0; j < m; ++j) {
            auto &t = !rev ? v[j] : v[m - 1 - j];
            if (t.l < i || t.l >= i + step) continue;
            while (R < t.r) {
                int x = a[++R];
                cnt[num[x]] -= 1;
                num[x] += 1;
                cnt[num[x]] += 1;
            }
            while (L > t.l) {
                int x = a[--L];
                cnt[num[x]] -= 1;
                num[x] += 1;
                cnt[num[x]] += 1;
            }
            while (R > t.r) {
                int x = a[R--];
                cnt[num[x]] -= 1;
                num[x] -= 1;
                cnt[num[x]] += 1;
            }
            while (L < t.l) {
                int x = a[L++];
                cnt[num[x]] -= 1;
                num[x] -= 1;
                cnt[num[x]] += 1;
            }
            ans[t.i] = cnt[t.k];
        }
    }
    for (int i = 0; i < m; ++i) printf("%d\n", ans[i]);
    return 0;
}
num[x]] -= 1;
                num[x] += 1;
                cnt[num[x]] += 1;
            }
            while (R > t.r) {
                int x = a[R--];
                cnt[num[x]] -= 1;
                num[x] -= 1;
                cnt[num[x]] += 1;
            }
            while (L < t.l) {
                int x = a[L++];
                cnt[num[x]] -= 1;
                num[x] -= 1;
                cnt[num[x]] += 1;
            }
            ans[t.i] = cnt[t.k];
        }
    }
    for (int i = 0; i < m; ++i) printf("%d\n", ans[i]);
    return 0;
}
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值