P4396 [AHOI2013] 作业(莫队+值域分块)

原题链接

P4396 [AHOI2013] 作业

题意

给定一个长度为 n n n ( n ≤ 1 e 5 n \leq 1e5 n1e5) 的序列 a n a_n an ( a i ≤ 1 e 5 a_i\leq 1e5 ai1e5), m m m ( m ≤ 1 e 5 m \leq 1e5 m1e5) 次询问,每次询问给定 l r a b,求区间 [ l , r ] [l,r] [l,r] 内大于等于 a a a 且小于等于 b b b 的数的个数,以及满足条件的不同数字的种数。

思路

容易想到莫队,但是对于区间的统计,如果直接莫队的话,需要使用树状数组 / 线段树等数据结构维护区间,时间复杂度会变成 O ( n m log ⁡ N ) O(n\sqrt m\log N) O(nm logN),显然无法接受。

朴素的想法,莫队的每次更新是 O ( 1 ) O(1) O(1) 的,查询是 O ( N ) O(N) O(N) 的。因为有 n m n\sqrt m nm 次更新操作和 m m m 次查询操作,显然更新比查询对复杂度的要求更苛刻。如果我们可以 O ( 1 ) O(1) O(1) 地更新、 O ( N ) O(\sqrt N) O(N ) 地查询,那么复杂度是可以接受的。

值域分块可以做到这一点。由于是单点更新,显然可以在 O ( 1 ) O(1) O(1) 的时间内完成对单点和块的更新,分块的查询是 O ( N ) O(\sqrt N) O(N ) 的,所以总复杂度为 O ( n m + m N ) O(n\sqrt m+m\sqrt N) O(nm +mN )

代码
#include <bits/stdc++.h>

constexpr int N = 100000;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n, m;
    std::cin >> n >> m;

    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
    }

    std::vector<std::array<int, 5>> Q(m);
    for (int i = 0; i < m; ++i) {
        std::cin >> Q[i][0] >> Q[i][1] >> Q[i][2] >> Q[i][3];
        Q[i][4] = i;
    }

    int len = std::sqrt(n);
    std::sort(Q.begin(), Q.end(), [&](auto x, auto y) {
        if ((x[0] - 1) / len != (y[0] - 1) / len) return x[0] < y[0];
        if ((x[0] - 1) / len & 1) return x[1] > y[1];
        return x[1] < y[1];
    });

    int siz = std::sqrt(N);
    int num = (N + siz - 1) / siz;
    std::vector<int> st(num + 1), ed(num + 1), id(N + 1);
    for (int i = 1; i <= num; ++i) {
        st[i] = ed[i - 1] + 1;
        ed[i] = std::min(st[i] + siz - 1, N);
        for (int j = st[i]; j <= ed[i]; ++j) {
            id[j] = i;
        }
    }

    std::vector<std::pair<int, int>> ans(m);
    std::vector<int> cnt(N + 1), sum(num + 1), ap(num + 1);

    auto add = [&](int x) {
        cnt[a[x]]++;
        sum[id[a[x]]]++;
        if (cnt[a[x]] == 1) ap[id[a[x]]]++;
    };

    auto del = [&](int x) {
        cnt[a[x]]--;
        sum[id[a[x]]]--;
        if (cnt[a[x]] == 0) ap[id[a[x]]]--;
    };

    auto query = [&](int fr, int to) -> std::pair<int, int> {
        int res1 = 0, res2 = 0;
        for (int i = fr; i <= std::min(ed[id[fr]], to); ++i) {
            if (cnt[i]) res1 += cnt[i], res2++;
        }
        if (id[fr] != id[to]) {
            for (int i = std::max(fr, st[id[to]]); i <= to; ++i) {
                if (cnt[i]) res1 += cnt[i], res2++;
            }
        }
        for (int i = id[fr] + 1; i < id[to]; ++i) {
            res1 += sum[i];
            res2 += ap[i];
        }
        return {res1, res2};
    };

    int l = 1, r = 0;
    for (auto [ql, qr, fr, to, id] : Q) {
        while (l > ql) add(--l);
        while (r < qr) add(++r);
        while (l < ql) del(l++);
        while (r > qr) del(r--);
        ans[id] = query(fr, to);
    }

    for (auto [x, y] : ans) {
        std::cout << x << " " << y << "\n";
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,我似乎没有找到与彩旗飘飘Python相关的引用内容。但是,如果你想在Python中实现彩旗飘飘的效果,你可以使用Pygame库来创建动画。以下是一个简单的示例代码,可以帮助你开始: ```python import pygame import random # 初始化pygame pygame.init() # 设置屏幕尺寸 screen_width = 800 screen_height = 600 screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption("彩旗飘飘") # 加载彩旗图片 flag_image = pygame.image.load("flag.png") # 获取彩旗图片的宽度和高度 flag_width = flag_image.get_width() flag_height = flag_image.get_height() # 设置彩旗的起始位置 flag_x = screen_width // 2 - flag_width // 2 flag_y = screen_height // 2 - flag_height // 2 # 设置彩旗的速度 flag_speed_x = random.randint(-5, 5) flag_speed_y = random.randint(-5, 5) # 游戏主循环 running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 移动彩旗 flag_x += flag_speed_x flag_y += flag_speed_y # 如果彩旗碰到边界,反转速度方向 if flag_x <= 0 or flag_x + flag_width >= screen_width: flag_speed_x = -flag_speed_x if flag_y <= 0 or flag_y + flag_height >= screen_height: flag_speed_y = -flag_speed_y # 清空屏幕 screen.fill((255, 255, 255)) # 绘制彩旗 screen.blit(flag_image, (flag_x, flag_y)) # 更新屏幕 pygame.display.flip() # 退出游戏 pygame.quit() ``` 请注意,你需要将代码中的"flag.png"替换为你自己的彩旗图片。此外,你还可以根据需要调整彩旗的起始位置、速度等参数。希望这个示例代码能帮助到你!<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [3d max制作彩旗飘飘](https://download.csdn.net/download/m0_71585230/88011500)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [[Rqnoj-371][AHOI1997]彩旗飘飘](https://blog.csdn.net/w745241408/article/details/7176600)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值