[DS记录]P5398 [Ynoi2018] GOSICK

command_block

2021-07-02 11:53:42

Personal

第十四分块。 **题意** : 给出一个长度为 $n$ 的序列 $A$。 每次询问给出 $l,r$ ,求 $l \leq i,j\leq r$,且 $A_i$ 是 $A_j$ (非严格)倍数的二元组 $(i,j)$ 的个数。 (不要求 $i\neq j$ , $(i,j)$ 和 $(j,i)$ 算两个不同的二元组) 允许离线, $n,m,A_i\leq 5\times 10^5$ ,时限$\texttt{3s}$,空限$\texttt{128M}$。 ------------ 认为 $n,m,A$ 同阶。 二次离线莫队。 先不考虑 $i=j$ 的 $(i,j)$ 的贡献,最后再将答案加上 $r-l+1$。 考虑从区间 $[l+1,r]$ 移动到 $[l,r]$ ,贡献的变化量为: $$\sum\limits_{l<i\leq r}[A_l|A_i]+[A_i|A_l]$$ 可以差分为 $$\sum\limits_{i\leq r}[A_p|A_i]+\sum\limits_{i\leq r}[A_i|A_p]$$ 分因数,倍数两部分处理。 - 倍数查询 维护 $w_x$ 表示目前为 $x$ 的倍数的数的个数。 插入一个数的复杂度为 $O(d(n))$ ,查询为 $O(1)$ ,套用二次离线即可做到 $O\Big(n\big(d(n)+\sqrt{n}\big)\Big)=O(n\sqrt{n})$。 - 因数查询 维护 $w_x$ 表示目前为 $x$ 的因数的数的个数。 插入数 $x$ 的复杂度为 $O(n/x)$ 。 考虑根号分治,对于 $>\sqrt{n}$ 的数,插入复杂度 $\leq O(\sqrt{n})$,用二次离线莫队处理。这部分复杂度为 $O(n\sqrt{n})$。 对于剩下 $\leq \sqrt{n}$ 的数,不在莫队中处理(莫队中只保留 $>\sqrt{n}$ 的数),分别直接考虑对询问的贡献。 数 $x$ 在询问 $[l,r]$ 中的贡献为 : $$ \begin{aligned} &\sum\limits_{l\leq i,j\leq r}[A_i=x][x|A_j]\\ =&\sum\limits_{l\leq i\leq r}[A_i=x]\sum\limits_{l\leq i\leq r}[x|A_i]\\ \end{aligned} $$ 只要知道 $[A_i=x]$ 与 $[x|A_i]$ 的区间和即可计算。对于每个 $x$ 分别处理区间和,然后贡献即可。 这部分的复杂度也是为 $O(n\sqrt{n})$ 。 一些卡常小技巧: - 对于数 $x$ ,若 $cnt_x*A/x>n$ 则特殊处理,反之用莫队处理。(代替根号分治无脑选 $\sqrt{n}$ 以下的策略) - 用 `vector` (或者压紧的数组)预处理每个数的约数。无需考虑特殊处理的数。 本来以为会很卡常,结果区区几发就过了,还是最优解…… ```cpp #include<algorithm> #include<cstdio> #include<vector> #include<cmath> #define ll long long #define pb emplace_back #define MaxN 500500 using namespace std; namespace io {}using namespace io; int lim,o[MaxN],d[MaxN<<4],pd[MaxN];bool fl[MaxN]; void InitD(int cost) { for (int i=1;i<=lim;i++) fl[i]=(1ll*o[i]*lim/i>cost)||!o[i]; for (int i=1;i<=lim;i++)if (!fl[i]) for (int j=i;j<=lim;j+=i)if (!fl[i]) pd[j]++; for (int i=1;i<=lim;i++)pd[i]+=pd[i-1]; for (int i=1;i<=lim;i++)if (!fl[i]) for (int j=i;j<=lim;j+=i)if (!fl[i]) d[++pd[j-1]]=i; for (int i=lim;i;i--)pd[i]=pd[i-1];pd[0]=0; } int t[MaxN]; inline void add(int x){ for (int i=pd[x-1]+1;i<=pd[x];i++)t[d[i]]++; for (int i=x;i<=lim;i+=x)t[i]++; } struct Data{int l,r,p;}; Data b[MaxN];int BS; bool cmp(const Data &A,const Data &B) {return A.l/BS==B.l/BS ? (A.l/BS)&1 ? A.r<B.r : A.r>B.r : A.l<B.l;} vector<Data> bl[MaxN],br[MaxN]; ll ans[MaxN];int sav[MaxN]; int n,m,a[MaxN]; void MoQ() { BS=n/sqrt(m)+1; sort(b+1,b+m+1,cmp); int l=1,r=0; for (int i=1;i<=m;i++){ if (b[i].l>b[i].r)continue; if (b[i].l<l){bl[r].pb((Data){b[i].l,l-1,b[i].p});l=b[i].l;} if (r<b[i].r){br[l].pb((Data){r+1,b[i].r,b[i].p});r=b[i].r;} if (l<b[i].l){bl[r].pb((Data){l,b[i].l-1,-b[i].p});l=b[i].l;} if (b[i].r<r){br[l].pb((Data){b[i].r+1,r,-b[i].p});r=b[i].r;} } #define ad(p,x) if (p<0)ans[-p]-=x;else ans[p]+=x; for (int i=1;i<=n;i++){ for (int j=0;j<br[i].size();j++){ Data &now=br[i][j];ll ret=0; for (int r=now.l;r<=now.r;r++)ret-=t[a[r]]; ad(now.p,ret); } add(a[i]);sav[i]=t[a[i]]; for (int j=0;j<bl[i].size();j++){ Data &now=bl[i][j];ll ret=0; for (int l=now.l;l<=now.r;l++)ret+=t[a[l]]-sav[l]; ad(now.p,ret); } } for (int i=1;i<=n;i++) for (int j=0;j<br[i].size();j++){ Data &now=br[i][j];ll ret=0; for (int r=now.l;r<=now.r;r++)ret+=sav[r]-2; ad(now.p,ret); } for (int i=1;i<=m;i++)ans[b[i].p]+=ans[b[i-1].p]; for (int i=1;i<=m;i++) if (b[i].l<=b[i].r)ans[b[i].p]+=b[i].r-b[i].l+1; else ans[b[i].p]=0; } int n0,a0[MaxN],tp[MaxN],s1[MaxN],s2[MaxN]; ll ans2[MaxN]; int main() { n0=rd();m=rd(); for (int i=1;i<=n0;i++){ o[a0[i]=rd()]++; lim=max(lim,a0[i]); } InitD((n0+m)*3); for (int i=1;i<=n0;i++){ if (!fl[a0[i]])a[++n]=a0[i]; tp[i]=n; } for (int i=1;i<=m;i++) {b[i].l=rd()-1;b[i].r=rd();} for (int x=1;x<=lim;x++)if (fl[x]&&o[x]){ for (int i=1;i<=n0;i++){ s1[i]=s1[i-1]+(a0[i]==x); s2[i]=s2[i-1]+(a0[i]%x==0); } for (int i=1;i<=m;i++) ans2[i]+=1ll*(s1[b[i].r]-s1[b[i].l])*(s2[b[i].r]-s2[b[i].l]); } for (int i=1;i<=m;i++){ b[i].l=tp[b[i].l]+1; b[i].r=tp[b[i].r]; b[i].p=i; } MoQ(); for (int i=1;i<=m;i++)print(ans[i]+ans2[i]),putc('\n'); flush(); return 0; } ```