题解:P5313 [Ynoi2011] WBLT
更好的阅读体验
@ForgetOIDuck 让我来做这道题,说这题难写难调还卡常。但是除了 bitset 的部分有点细节以外,其他并不是特别难写;然后拍了若干组只找出一个糖错;最后也没有非常严重地被卡常。
总之是非常小清新的一道题,为出题人点赞!!111
假定序列长度,询问次数,值域同阶。
假设我们手里已经有了区间中出现的数字集合
我们可以将
我们没有什么很好的结构来维护集合,因此考虑 bitset。接下来考虑怎么由
那么我们使用一个普通莫队动态维护这个
由于 bitset 的 set(x) 操作和 reset(x) 操作都可以做到
这并不好,当
接下来分析跑
至此,问题在
由于需要支持将 split(l,r) 函数是将原 bitset 的
#include<bits/stdc++.h>
#define endl '\n'
#define N 100006
using namespace std;
using u64=unsigned long long;
u64 all1[65];
struct Bitset {
int sz,len; vector<u64> b;
Bitset(int n=0):sz(n) {len=(sz>>6)+1,b.resize(len+5); for(int i=0;i<=len;i++)b[i]=0;}
inline void set(int i) {b[i>>6]|=1ull<<(i&63);}
inline void reset(int i) {b[i>>6]&=~(1ull<<(i&63));}
inline void set() {for(int i=0;i<=len;i++)b[i]=~0ull;}
inline void reset() {for(int i=0;i<=len;i++)b[i]=0ull;}
inline bool any() {for(int i=0;i<=len;i++)if(b[i])return 1; return 0;}
inline void operator &=(Bitset x) {for(int i=0;i<=len&&i<=x.len;i++)b[i]&=x.b[i];}
inline int mex() {for(int i=0;i<=len;i++)if(~b[i])return (i<<6)|__builtin_ctzll(~b[i]);}
inline Bitset split(int l,int r)
{
int now=0; Bitset res(r-l+1);
for(int i=l,c=0;i<=r;i+=64,c++)
{
int lb=i,rb=min(i+63,r);
if(!((lb^rb)>>6))
{
int bel=lb>>6; lb&=63,rb&=63;
res.b[c]=(b[bel]<<(63-rb))>>(lb+63-rb);
} else {
int bel1=lb>>6,bel2=rb>>6; lb&=63,rb&=63;
res.b[c]=((b[bel1]>>lb)&all1[63-lb])|((b[bel2]&all1[rb])<<(64-lb));
}
}
return res;
}
inline int query(int k)
{
Bitset now(k); now.set();
int ret=0;
for(int l=0,r;l<=sz;l=r+1,ret++)
{
r=l+k-1,now&=split(l,r);
if(!now.any())break;
}
return ret;
}
};
struct Ask {int l,r,b,id;};
int n,q,Block,a[N],bel[N],ans[N],buc[N];
Bitset bt1(N+N+64),bt2[70];
vector<Ask> ask[70];
inline void add_big(int x) {if(!buc[x]++)bt1.set(x);}
inline void del_big(int x) {if(!--buc[x])bt1.reset(x);}
inline void add_small(int x,int b) {if(!buc[x]++)bt2[x%b].set(x/b);}
inline void del_small(int x,int b) {if(!--buc[x])bt2[x%b].reset(x/b);}
void solve_big()
{
if(!ask[0].size())return;
Block=max<int>(1,n/sqrt((int)ask[0].size()));
for(int i=1;i<=n;i++)bel[i]=(i-1)/Block+1;
sort(ask[0].begin(),ask[0].end(),[](Ask x,Ask y) {
if(bel[x.l]!=bel[y.l])return bel[x.l]<bel[y.l];
return bel[x.l]&1?x.r<y.r:x.r>y.r;
});
int lb=1,rb=0;
for(auto _:ask[0])
{
int l=_.l,r=_.r,b=_.b,id=_.id;
for(;lb>l;add_big(a[--lb]));
for(;rb<r;add_big(a[++rb]));
for(;lb<l;del_big(a[lb++]));
for(;rb>r;del_big(a[rb--]));
ans[id]=bt1.query(b);
}
}
void solve_small(int b)
{
if(!ask[b].size())return;
Block=max<int>(1,n/sqrt((int)ask[b].size()));
for(int i=1;i<=n;i++)bel[i]=(i-1)/Block+1;
for(int i=0;i<N;i++)buc[i]=0;
for(int i=0;i<b;i++)bt2[i]=Bitset(N/b+1);
sort(ask[b].begin(),ask[b].end(),[](Ask x,Ask y) {
if(bel[x.l]!=bel[y.l])return bel[x.l]<bel[y.l];
return bel[x.l]&1?x.r<y.r:x.r>y.r;
});
int lb=1,rb=0;
for(auto _:ask[b])
{
int l=_.l,r=_.r,id=_.id;
for(;lb>l;add_small(a[--lb],b));
for(;rb<r;add_small(a[++rb],b));
for(;lb<l;del_small(a[lb++],b));
for(;rb>r;del_small(a[rb--],b));
for(int i=0;i<b;i++)ans[id]=max(ans[id],bt2[i].mex());
}
}
main()
{
scanf("%d",&n),all1[0]=1;
for(int i=1;i<64;i++)all1[i]=all1[i-1]*2+1;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
scanf("%d",&q);
for(int i=1,l,r,b;i<=q;i++)
{
scanf("%d%d%d",&l,&r,&b);
if(b>64)ask[0].push_back({l,r,b,i});
else ask[b].push_back({l,r,b,i});
}
solve_big();
for(int i=1;i<=64;i++)solve_small(i);
for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
return 0;
}