[Ynoi Easy Round 2016] 掉进兔子洞 解题报告
[Ynoi Easy Round 2016] 掉进兔子洞 解题报告
简要题意
在一个长度为 \(n\) 的序列中,有 \(m\) 次询问,每次询问三个区间,求出这三个区间中相同元素的个数。多次询问,询问独立。
其中:\(n,m \le 10^5\)
分析
套路地,注意到 \(n \le 10^5\),便想到用 \(\text{bitset}\) 维护数字出现情况。
但是本题不同于一般情况下用 \(bitset\) 维护数字出现情况,本题中序列中元素构成的全集是一个可重集。
但是全集是已知的,因此我们可以把一个数字 \(i\) 存入 \(\text{小于i的数的个数} + \text{之前i的出现次数}\),可以证明,这样做没有两个数字会存在同一个位置,并且 \(\text{bitset}\) 的大小是 \(n\)。
接下来便是考虑询问。虽然每一次询问涉及三个区间中的数字出现情况,但是我们发现最后三个区间的相同元素个数可以由每个区间的元素出现情况合并得到。如此一来我们便可以使用莫队处理询问。
但是还有一个问题,我们记录答案时使用的 \(\text{bitset}\) 长度是 \(n\),共有 \(m\) 次询问,这意味着我们需要开 \(nm\) 个 \(\text{bool}\) 类型的变量,也就是 \(10^{10} B\) ,接近 \(1 GB\)。这显然是不可直接做的。
注意到询问独立,因此我们可以把询问平均分成三部分,每一部分单独做一次莫队,这样我们的空间也就只需要开到原来的三分之一,这样就满足空间限制了。代价是莫队会多一点时间常数,但不影响通过本题。
这本质是时间换空间的做法。
代码
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define For(i,s,t) for(int i=s;i<=t;++i)
#define Down(i,s,t) for(int i=s;i>=t;--i)
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef pair<int,pair<int,int> > piii;
typedef long long ll;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
inline int read(){
register int x=0;
char c=getchar();
while(c<'0' || '9'<c) c=getchar();
while('0'<=c && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
return x;
}
void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>=10) write(x/10);
putchar(x%10+'0');
}
const int N=1e5+100;
int n,m,a[N],rk[N],cnt[N],st[N],len,sz,ans[N];
bitset<N> exist[N/3+1000],nw;
void init(){
For(i,1,n) rk[i]=a[i];
sort(rk+1,rk+n+1,less<int>());
int ind=0;
For(i,1,n)
if(rk[i]!=rk[i-1])
st[++ind]=i;
len=unique(rk+1,rk+n+1)-rk-1;
For(i,1,n) a[i]=lower_bound(rk+1,rk+len+1,a[i])-rk;
sz=n/sqrt(n);
}
struct Node{int l,r,id;}q[N*3];
bool cmp(Node x,Node y){
return (x.l/sz!=y.l/sz ? x.l<y.l : ( (x.l/sz)%2 ? x.r<y.r : x.r>y.r));
}
void add(int x){
nw[st[x]+cnt[x]-1]=true;
cnt[x]++;
}
void del(int x){
cnt[x]--;
nw[st[x]+cnt[x]-1]=false;
}
void solve(int k){
int l=1,r=0;
For(i,1,len) cnt[i]=0;
nw.reset();
For(i,1,k){
//先加后减
while(r<q[i].r) add(a[++r]);
while(q[i].l<l) add(a[--l]);
while(q[i].r<r) del(a[r]),--r;
while(l<q[i].l) del(a[l]),++l;
exist[q[i].id]&=nw,ans[q[i].id]+=r-l+1;
//printf("%d %d %d\n",l,r,nw.count());
}
For(i,1,k/3)
printf("%d\n",ans[i]-3*exist[i].count());
}
int main()
{
//freopen("road.in","r",stdin);
//freopen("bike.out","w",stdout);
n=read(),m=read();
For(i,1,n) a[i]=read();
init();
if(m>=10){
For(k,1,3){
int st=m*(k-1)/3+1,ed=m*k/3,ind=0;
For(i,st,ed){
++ind;
q[ind*3-2].l=read(),q[ind*3-2].r=read(),q[ind*3-2].id=ind;
q[ind*3-1].l=read(),q[ind*3-1].r=read(),q[ind*3-1].id=ind;
q[ind*3 ].l=read(),q[ind*3 ].r=read(),q[ind*3 ].id=ind;
exist[ind].set();
ans[ind]=0;
}
//if(k==2){
// For(i,1,ind*3)
// printf("%d %d %d\n",q[i].l,q[i].r,q[i].id);
//}
sort(q+1,q+ind*3+1,cmp);
solve(ind*3);
}
}
else{
int st=1,ed=m,ind=0;
For(i,st,ed){
++ind;
q[ind*3-2].l=read(),q[ind*3-2].r=read(),q[ind*3-2].id=ind;
q[ind*3-1].l=read(),q[ind*3-1].r=read(),q[ind*3-1].id=ind;
q[ind*3 ].l=read(),q[ind*3 ].r=read(),q[ind*3 ].id=ind;
exist[ind].set();
}
sort(q+1,q+ind*3+1,cmp);
solve(ind*3);
}
return 0;
}