zoukankan      html  css  js  c++  java
  • BZOJ4939[Ynoi2016]掉进兔子洞(莫队+bitset)

    题目链接

    BZOJ

    洛谷

    扯点别的

    听说这是比较亲民的一道Ynoi,于是我就去摸了一下,于是一个晚上就没了……不过至少还是学到了(bitset)维护可重集合的方法,以及当空间开不下时分组处理询问的花操作……

    解析

    大佬们说容易想到bitset,那就想到bitset吧……

    这种形如“几个集合共同含有的元素”的问题容易让人想(bitset)搞一搞,但是我们发现裸的(bitset)只能表示有没有,不能表示有多少,下面就是神奇的让(bitset)能表示有多少的方法

    就这道题而言首先肯定是离散化,但是离散化时我们不去重,这样相同元素会是连续的一段,我们取第一个出现的位置作为离散化后的值,简单举个例子:1,4,2,4,4,5 => hash[1] = 0,hash[2] = 1,hash[4] = 2,hash[5] = 5

    假设我们要加入一个数(x),它之前已经在这个集合中出现了(cnt)次,那么我们把(x + cnt)那一位置为(1),删除同理,即:bitset中第(i + j)个位置表示数字(i)(j + 1)次出现

    这样当我们把每个(bitset)与起来就相当于对每个数的出现次数取了个(min)

    回到题目,假设我们知道了三个区间的(bitset)与起来是(S),那么答案显然就是(len1 + len2 + len3 - |S| imes 3)

    我们发现(bitset)加入和删除都能很快处理,询问的又是区间,那么就可以上莫队试试了,莫队的时候维护一下每个数出现的次数即可

    还有一点,直接开(1e5)(bitset)肯定开不下,于是我们把询问分组,每组上一次莫队即可

    分组大小可以算算,我比较懒就从(1e4)开始试然后到(3e4)的时候就过了……

    代码

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <bitset>
    #include <algorithm>
    #define MAXN 100003
    #define belong(x) ((x) / per_block)
    
    typedef long long LL;
    struct Query {
    	int l, r, id;
    } qry[90005];
    int arr[MAXN], hash[MAXN], cnt[MAXN], N, M, per_block, idx;
    std::bitset<MAXN> cur, ans[30003];
    bool vis[30003];
    
    bool operator <(const Query &q1, const Query &q2) {
    	return belong(q1.l) == belong(q2.l) ? q1.r < q2.r : belong(q1.l) < belong(q2.l);
    }
    bool cmp(const Query &q1, const Query &q2) { return q1.id < q2.id; }
    
    int main() {
    	scanf("%d%d", &N, &M);
    	for (int i = 0; i < N; ++i) scanf("%d", arr + i), hash[i] = arr[i];
    	std::sort(hash, hash + N);
    	for (int i = 0; i < N; ++i)
    		arr[i] = std::lower_bound(hash, hash + N, arr[i]) - hash;
    	while (per_block * per_block < N) ++per_block;
    
    	while (M) {
    		memset(cnt, 0, sizeof cnt);
    		memset(vis, 0, sizeof vis);
    		cur.reset();
    		int tot = std::min(30000, M);
    		for (int k = 0; k < tot * 3; ++k) scanf("%d%d", &qry[k].l, &qry[k].r), qry[k].id = k / 3;
    		std::sort(qry, qry + tot * 3);
    		for (int l = 0, r = -1, i = 0; i < tot * 3; ++i) {
    			--qry[i].l, --qry[i].r;
    			while (r < qry[i].r) ++r, cur[arr[r] + cnt[arr[r]]] = 1, ++cnt[arr[r]];
    			while (r > qry[i].r) --cnt[arr[r]], cur[arr[r] + cnt[arr[r]]] = 0, --r;
    			while (l < qry[i].l) --cnt[arr[l]], cur[arr[l] + cnt[arr[l]]] = 0, ++l;
    			while (l > qry[i].l) --l, cur[arr[l] + cnt[arr[l]]] = 1, ++cnt[arr[l]];
    			if (!vis[qry[i].id]) vis[qry[i].id] = 1, ans[qry[i].id] = cur;
    			else ans[qry[i].id] &= cur;
    		}
    		std::sort(qry, qry + tot * 3, cmp);
    		for (int i = 0; i < tot; ++i) {
    			int p1 = i * 3, p2 = p1 + 1, p3 = p2 + 1;
    			printf("%d
    ", qry[p1].r - qry[p1].l + qry[p2].r - qry[p2].l + qry[p3].r - qry[p3].l + 3 - (int)ans[i].count() * 3);
    		}
    		M -= tot;
    	}
    
    	return 0;
    }
    //Rhein_E
    
  • 相关阅读:
    getopt( )和 getopt_long( )
    关于跳跃表 转
    进程控制块的存放和当前进程的确定
    BUAA_OO_2020_Unit1 Summary
    熟悉常用的Linux操作
    编译原理
    词法分析
    组合数据类型练习
    实验一 词法分析实验
    简化版C语言文法
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10506971.html
Copyright © 2011-2022 走看看