zoukankan      html  css  js  c++  java
  • YNOI2016:掉进兔子洞 (莫队+bitset)

    YNOI2016:掉进兔子洞

    题意简述:

    有 m 个询问,每次询问三个区间,把三个区间中同时出现的数一个一个删掉,问最后三个区间剩下的数的个数和,询问独立。 注意这里删掉指的是一个一个删,不是把等于这个值的数直接删完,比如三个区间是 $ [1,2,2,3,3,3,3] $ , $ [1,2,2,3,3,3,3] $ 与 $ [1,1,2,3,3] $ ,就一起扔掉了 $ 1 $ 个 $ 1 (,) 1 $ 个 $ 2 (,) 2 $ 个 $ 3 $ 。



    $ solution: $

    考场上觉得是毒瘤容斥+莫队(先求两两区间,得到三个区间),但是这道题不能这样容斥,有两个未知量。

    其实重点就在于序列的存储的方式,然后就是我们对于莫队+ $ bitset $ 的熟悉度。我们知道如果相同元素在单个区间里只出现一次,那么我们直接对每个区间是否拥有某个元素二进制状压,然后三个区间与运算得到重复元素的信息(可以用 $ bitset $ 维护)。但是相同元素在单个区间里会出现多次,这个有一个很妙的维护方法:我们排序将相同元素放一块,然后对于每一段相同元素记录第一个位置,于是我们在 $ bitset $ 数组中就可以用对应的一段区间来维护这种相同元素出现次数(从区间第一个位置开始,每加入一个这个元素,就将后一个位置变为1)

    然后多个 $ bitset $ 与运算,为一的位置说明在多个区间都有这个元素。对于区间的二进制状压信息,我们可以用莫队算法来求,因为对每个询问都要 $ bitset $ 数组,时间没问题但空间开不下,于是将询问分组做。



    $ code: $

    #include<iostream>
    #include<cstdio>
    #include<iomanip>
    #include<algorithm>
    #include<bitset>
    #define ll long long
    #define rg register int
    
    using namespace std;
    
    int n,m,M,ff;
    int a[100005];
    int p[100005];
    int A[100005];
    int ans[100005];
    bool vis[25005];
    bitset<100005> b[25005],s;
    
    struct su{
    	int x,y,id,v;
    	inline bool operator <(const su &i){
    		if(v==i.v){
    			if(v&1) return y<i.y;
    			return y>i.y; //奇偶分块优化常数
    		} return v<i.v;
    	}
    }q[100005];
    
    inline int qr(){
        register char ch; register bool sign=0; rg res=0;
        while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
        while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
        if(sign)return -res; else return res;
    }
    
    int main(){
    	n=qr(); M=m=qr(); ff=pow(n-1,0.5)+1; //分块
    	for(rg i=1;i<=n;++i) A[i]=a[i]=qr();
    	sort(A+1,A+n+1); //离散化,顺带把相同元素放一起并记录第一个位置
    	for(rg i=1;i<=n;++i) a[i]=lower_bound(A+1,A+n+1,a[i])-A;
    	while(M){
    		m=min(M,25000); M-=m; rg tt=0; //问询分组处理
    		for(rg i=1;i<=n;++i) p[a[i]]=a[i];
    		for(rg i=1;i<=m;++i){
    			rg l1=qr(),r1=qr(),l2=qr(),r2=qr(),l3=qr(),r3=qr();
    			ans[i]=r1+r2+r3-l1-l2-l3+3; vis[i]=0;
    			q[++tt]=su{l1,r1,i,(l1-1)/ff+1}; //最后一个元素分块
    			q[++tt]=su{l2,r2,i,(l2-1)/ff+1};
    			q[++tt]=su{l3,r3,i,(l3-1)/ff+1};
    		} sort(q+1,q+tt+1); //分块排序
    		rg l=1,r=0; s.reset();
    		for(rg i=1;i<=tt;++i){
    			rg x=q[i].x,y=q[i].y,id=q[i].id;
    			while(x<l)--l,s[p[a[l]]++]=1; //莫队
    			while(r<y)++r,s[p[a[r]]++]=1;
    			while(l<x)s[--p[a[l]]]=0,++l;
    			while(y<r)s[--p[a[r]]]=0,--r;
    			if(vis[id]) b[id]&=s; //&,三个集合都要有
    			else vis[id]=1,b[id]=s; //第一个直接覆盖(都不需要预处理)
    		}
    		for(rg i=1;i<=m;++i) //答案=总数-不合法数
    			printf("%d
    ",ans[i]-(int)b[i].count()*3);
    	}
        return 0;
    }
    
    
  • 相关阅读:
    Codeforces Round #174 (Div. 1 + Div. 2)
    Codeforces Round #176 (Div. 1 + Div. 2)
    [ARC101C] Ribbons on Tree 解题报告
    [CTS2019]珍珠 解题报告
    CF1349D Slime and Biscuits 解题报告
    [PKUWC2018]猎人杀 解题报告
    AGC047 解题报告
    肯德基 解题报告
    [GXOI/GZOI2019]旧词 解题报告
    [ARC084B] Small Multiple 解题报告
  • 原文地址:https://www.cnblogs.com/812-xiao-wen/p/11709260.html
Copyright © 2011-2022 走看看