首页 > 代码库 > BZOJ 4939 [Ynoi2016]掉进兔子洞(莫队+bitset)
BZOJ 4939 [Ynoi2016]掉进兔子洞(莫队+bitset)
【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=4939
【题目大意】
给出一个数列,每个询问给出三个区间,问除去三个区间共有的数字外,
还剩下几个数字,注意删去的是共有的数字个数,不是数字种类,统计时候也一样
【题解】
首先,答案为区间长度和减去区间并数字个数和的三倍。
所以题目转化为求区间并。很显然在开始对数据可以进行离散化。
考虑每个数字只出现一次的情况,我们可以用bitset来统计区间某个数字是否存在,
莫队处理查询每个区间,保存其bitset的值,最后求交即可,
现在考虑每个数字出现多次的情况,
我们发现经过离散的数据之间空位数量恰好可以用来标出现多次的数据,
比如1 4 4 9 9,离散后为 1 2 2 4 4,
我们可以将多出来的2标在3位置,4标在5位置,那么就可以用bitset统计了。
- Me : 询问区间存不下怎么办?
- Claris :将询问分批进行处理,单次处理25000个询问
- Me : 超时了欸……
- Claris : 这题卡常数,要手写bitset.
- Me : 你的代码为什么有6.7k?
- Claris :我分出现一次,两次和跟多次讨论
- Me : 我……还是咸鱼吧……
【代码】
#include <cstdio>#include <algorithm>#include <bitset>#include <cmath>using namespace std;typedef unsigned long long ULL;const int N=100010,M=N<<2;int limit,n,m,pos[N],a[N],cnt[N],Ans[N],mark[N];struct Q{ int l,r,id; friend bool operator < (const Q &a,const Q &b){ return pos[a.l]<pos[b.l]||(pos[a.l]==pos[b.l]&&a.r<b.r); }}ask[M];int read(int &x){ int f=1;char ch=getchar();x=0; while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} x*=f;}int disc[N];int remark(int x){ int l=1,r=n,res=0; while(l<=r){ int mid=(l+r)>>1; if(disc[mid]<x)l=mid+1; else res=mid,r=mid-1; }return res;}const int B=1567,K=25000;ULL v[B],f[K+3][B];int u[65537],tmp,U;void flip(int x){v[x>>6]^=1ULL<<(x&63);}void Copy(ULL*a){ int i=0; for(;i+13<=U;i+=14){ for(int j=0;j<14;j++)a[i+j]=v[i+j]; }for(;i<=U;i++)a[i]=v[i];}void And(ULL*a){ int i=0; for(;i+13<=U;i+=14){ for(int j=0;j<14;j++)a[i+j]&=v[i+j]; }for(;i<=U;i++)a[i]&=v[i];}void popcount(ULL x){tmp+=u[x&65535]+u[x>>16&65535]+u[x>>32&65535]+u[x>>48];}int count(ULL*a){ int i=tmp=0; for(;i+13<=U;i+=14){ for(int j=0;j<14;j++)popcount(a[i+j]); }for(;i<=U;i++)popcount(a[i]); return tmp;}void init(){for(int i=1;i<65536;i++)u[i]=u[i>>1]+(i&1);} int main(){ read(n); read(m); U=n>>6; init(); limit=(int)sqrt(n+0.5); for(int i=1;i<=n;i++)read(a[i]),disc[i]=a[i],pos[i]=(i-1)/limit+1; sort(disc+1,disc+n+1); for(int i=1;i<=n;i++)a[i]=remark(a[i]); //for(int i=1;i<=n;i++)printf("%d\n",a[i]); int pos=0,l=1,r=0; while(pos<m){ int tot=0; for(int i=1;i<=25000&&i+pos<=m;i++){ tot+=3; Ans[i]=0; mark[i]=0; read(ask[i*3-2].l); read(ask[i*3-2].r); ask[i*3-2].id=i; read(ask[i*3-1].l); read(ask[i*3-1].r); ask[i*3-1].id=i; read(ask[i*3].l); read(ask[i*3].r); ask[i*3].id=i; Ans[i]+=ask[i*3-2].r-ask[i*3-2].l+1; Ans[i]+=ask[i*3-1].r-ask[i*3-1].l+1; Ans[i]+=ask[i*3].r-ask[i*3].l+1; }sort(ask+1,ask+tot+1); for(int i=1;i<=tot;i++){ for(;r<ask[i].r;r++){flip(a[r+1]+cnt[a[r+1]]);cnt[a[r+1]]++;} for(;l>ask[i].l;l--){flip(a[l-1]+cnt[a[l-1]]);cnt[a[l-1]]++;} for(;l<ask[i].l;l++){cnt[a[l]]--;flip(a[l]+cnt[a[l]]);} for(;r>ask[i].r;r--){cnt[a[r]]--;flip(a[r]+cnt[a[r]]);} if(mark[ask[i].id])And(f[ask[i].id]); else Copy(f[ask[i].id]),mark[ask[i].id]=1; }tot/=3; for(int i=1;i<=tot;i++)printf("%d\n",Ans[i]-3*count(f[i])); pos+=tot; }return 0;}
BZOJ 4939 [Ynoi2016]掉进兔子洞(莫队+bitset)
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。