题解:P5313 [Ynoi2011] WBLT
@ForgetOIDuck 让我来做这道题,说这题难写难调还卡常。但是除了 bitset 的部分有点细节以外,其他并不是特别难写;然后拍了若干组只找出一个糖错;最后也没有非常严重地被卡常。
总之是非常小清新的一道题,为出题人点赞!!111
假定序列长度,询问次数,值域同阶。
假设我们手里已经有了区间中出现的数字集合 \(S\),要如何求出最长的 \(\bmod \space b\) 同余的前缀?
我们可以将 \(S\) 中元素按照值域分组。具体地,\(S_k\) 表示 \(S\) 中属于 \([kb, (k+1) b)\) 的数字构成的子集。那么答案就是最大的 \(k\) 使得 \(S_0 \cap S_1 \cap S_2 \cdots \cap S_{k-1}\) 非空的 \(k\)。
我们没有什么很好的结构来维护集合,因此考虑 bitset。接下来考虑怎么由 \(S\) 分出 \(S_0, S_1, \cdots\)。这个过程可以在 \(O\left(\frac{|S|}{\min(w, b)}\right)\) 的时间内实现。
那么我们使用一个普通莫队动态维护这个 \(S\)。具体地,我们再多维护一个每个数字出现次数的桶,每插入一个数字 \(x\) 的时候,如果这个数字未出现过,就将其插入到 \(S\) 中;每删除一个数字 \(x\) 的时候,如果删掉它之后区间中不存在数字 \(x\) 了,那么就将 \(x\) 从 \(S\) 中删除。
由于 bitset 的 set(x) 操作和 reset(x) 操作都可以做到 \(O(1)\),因此莫队部分可以在 \(O(n \sqrt n)\) 的时间内解决。到这里我们做到了 \(O\left(n \sqrt n + \sum\frac{n}{\min(w, b)}\right)\)。
这并不好,当 \(b\) 很小的时候就接近 \(O(n^2)\)。我们对 \(b\) 进行阈值分治,如果 \(b > w\) 那么我们就跑刚才的过程,这没问题,是 \(O\left(n \sqrt n + \frac{n^2}{w} \right)\) 的。对于 \(b \le w\) 的情况,我们把询问挂在 \(b\) 上,然后对于所有 \(b\) 相同的询问分别跑一次莫队。具体地,我们维护 \(b\) 个 bitset,对于一个数字 \(x\),它会被储存在第 \(x \bmod b\) 的第 \(x / b\) 个位置。查询的时候,我们枚举余数 \(i\),那么余数为 \(i\) 时的答案就相当于是对第 \(i\) 个 bitset 中的元素求 mex。我们知道对一个大小为 \(k\) 的 bitset 求 mex 可以做到 \(O\left(\frac{k}{w} \right)\) 的复杂度。我们需要遍历 \(b\) 个 bitset,每个 bitset 的大小是 \(\frac{n}{b}\),因此对每一个询问,查询的时间可以做到 \(O\left(b \times \frac{n}{bw}\right) = O\left(\frac{n}{w}\right)\)。
接下来分析跑 \(w\) 次莫队的复杂度,很多题解在这个地方的分析有误。假设 \(b=i\) 的询问有 \(c_i\) 组,那么跑 \(w\) 次莫队的时间应该是 \(O\left(\sum_{i \le w} n \sqrt {c_i}\right)\)。容易发现该式在 \(c_1 = c_2 \cdots = c_w = \frac{n}{w}\) 的时候取 max。此时的复杂度是 \(O\left(wn \sqrt {\frac{n}{w}}\right) = O(n \sqrt{wn})\)。
至此,问题在 \(O\left(n \sqrt{wn} + \frac{n^2}{w}\right)\) 的时间内得到解决。
由于需要支持将 \(S\) 按照值域划分成若干个子集,因此需要手写 bitset。代码中的 split(l,r) 函数是将原 bitset 的 \([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;
}

浙公网安备 33010602011771号