6.8 NOI 模拟
\(T1\ edge\)
考虑\(O(q\times n\times \log n)\)的暴力
暴力二分,直接树上差分
#define Eternal_Battle ZXK
#include
#define MAXN 50005
using namespace std;
int head[MAXN],nxt[MAXN<<1],to[MAXN<<1],tot;
int dep[MAXN],tag[MAXN],val[MAXN],f[MAXN][22],n,m,q;
struct node
{
int x,y;
bool s;
}li[MAXN];
void add(int u,int v)
{
tot++;
to[tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
void dfs_pre(int now,int fa)
{
f[now][0]=fa;
dep[now]=dep[fa]+1;
for(int i=1;i<=20;i++)
{
f[now][i]=f[f[now][i-1]][i-1];
}
for(int i=head[now];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
dfs_pre(y,now);
}
}
int LCA(int x,int y)
{
if(dep[x]=0;i--)
{
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
}
if(x==y) return x;
for(int i=20;i>=0;i--)
{
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int dfs(int now,int fa)
{
int res=0;
for(int i=head[now];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
res+=dfs(y,now);
}
res+=tag[now];
val[now]=res;
return res;
}
int lca[3005][3005];
bool check(int x,int r)
{
memset(val,0,sizeof(val));
memset(tag,0,sizeof(tag));
for(int i=x;i<=r;i++)
{
if(!li[i].s) continue;
int u=li[i].x,v=li[i].y;
tag[u]+=1; tag[v]+=1;
tag[lca[u][v]]+=-1;
tag[f[lca[u][v]][0]]+=-1;
}
dfs(1,1);
for(int i=1;i<=n;i++)
{
if(!val[i]) return false;
}
return true;
}
int sol(int num)
{
int l=1,r=num,mid,ans=0;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid,num))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
return ans;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1,u,v;i
这样是没有前途的
观察数据范围,发现大概可能是个\(O(n\sqrt n \log n)\)
考虑这样一个暴力,从前往后扫,树剖维护链覆盖,取全局最小值即可
考虑区间分块,每个块尾维护一个前缀的树剖,空间复杂度\(O(n\sqrt n)\)
每次修改的话就是,把后面的块都暴力修改一遍,单次复杂度\(O(\sqrt n \log n)\)
查询的话就是找最近的一个块往后移动,查询全局最小值即可,然后\(vector\)撤回操作,单次复杂度\(O(\sqrt n \log n)\)
根据常数大小获得\(60pts\sim 100pts\)
代码,\(std\)的
#include
#include
#include
#include
const int Maxn=50000;
const int Maxq=100000;
const int Maxb=1500;
const int Maxv=(Maxn-1)/Maxb+1;
const int Inf=0x3f3f3f3f;
int n,m,q;
struct Edge{
int u,v;
}edge[Maxn+5];
bool vis_op[Maxn+5];
int op_id[Maxq+5];
int ans[Maxq+5];
int find_belong(int x){
return (x-1)/Maxb+1;
}
int find_bel_l(int x){
return (x-1)*Maxb+1;
}
int find_bel_r(int x){
return std::min(m,x*Maxb);
}
int head[Maxn+5],arrive[Maxn<<1|5],nxt[Maxn<<1|5],tot;
void add_edge(int from,int to){
arrive[++tot]=to;
nxt[tot]=head[from];
head[from]=tot;
}
int fa[Maxn+5],sz[Maxn+5],son[Maxn+5],dep[Maxn+5],top[Maxn+5];
int dfn[Maxn+5],rnk[Maxn+5],dfn_tot;
void init_dfs_1(int u){
sz[u]=1;
dep[u]=dep[fa[u]]+1;
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
fa[v]=u;
init_dfs_1(v);
sz[u]+=sz[v];
if(son[u]==0||sz[v]>sz[son[u]]){
son[u]=v;
}
}
}
void init_dfs_2(int u,int tp){
dfn[u]=++dfn_tot;
rnk[dfn[u]]=u;
top[u]=tp;
if(son[u]){
init_dfs_2(son[u],tp);
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]||v==son[u]){
continue;
}
init_dfs_2(v,v);
}
}
int find_lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]1){
vis[u]=1;
vis_up[u]=vis_down[u]=u;
}
}
void vir_dfs_2(int u){
if(vis_up[u]&&vis_down[u]){
on_path[u]=1;
if(!vis[u]){
vis_up[u]=vis_up[fa[u]];
}
}
else{
if(on_path[fa[u]]){
vis_up[u]=fa[u];
}
else{
vis_up[u]=vis_up[fa[u]];
}
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
vir_dfs_2(v);
}
}
namespace Subtask_1{
int val_num[Maxn+5],val_pos;
struct Node{
int ch[2];
int dep_l,dep_r;
int cur_val,mn_val,lazy;
}node[Maxn+5];
int lis[Maxn+5],sum[Maxn+5],root[Maxn+5],mn_n[Maxn+5];
int len;
int build_line(int l,int r){
if(l>r){
return 0;
}
int left=l,right=r;
while(leftnode[root].dep_r||l>r||val<=node[root].lazy){
return;
}
if(l<=node[root].dep_l&&r>=node[root].dep_r){
update_tag(root,val);
return;
}
if(l<=dep[root]&&r>=dep[root]){
node[root].cur_val=std::max(node[root].cur_val,val);
}
_update(node[root].ch[0],l,r,val),_update(node[root].ch[1],l,r,val);
push_up(root);
}
void update(int root,int l,int r,int val){
val_num[node[root].mn_val]--;
_update(root,l,r,val);
val_num[node[root].mn_val]++;
while(val_pos<=m&&val_num[val_pos]==0){
val_pos++;
}
}
void modify_root(int u,int val){
while(mn_n[u]){
update(root[u],dep[mn_n[u]],dep[u],val);
u=fa[mn_n[u]];
}
}
void modify_path(int u,int v,int val){
while(mn_n[u]!=mn_n[v]){
if(dep[mn_n[u]]dep[v]){
std::swap(u,v);
}
update(root[u],dep[u]+1,dep[v],val);
}
void init_dfs(int u){
if(vis_down[u]==0){
build_tree(u);
return;
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
init_dfs(v);
}
}
void init(){
for(int i=1;i<=n;i++){
mn_n[i]=0;
}
node[0].mn_val=Inf;
val_pos=0;
for(int i=0;i<=m;i++){
val_num[i]=0;
}
init_dfs(1);
while(val_pos<=m&&val_num[val_pos]==0){
val_pos++;
}
}
}
namespace Subtask_2{
struct Segment_Tree{
int n;
std::vector val;
void init(int _n){
n=_n;
val.clear();
val.resize(n*2+2);
val[n*2]=Inf;
}
void update(int l,int r,int a){
if(l>r){
return;
}
int s_l=l+n-1,s_r=r+n-1;
for(l+=n-1,r+=n;l>=1,r>>=1){
if(l&1){
val[l]=std::max(val[l],a);
l++;
}
if(r&1){
r--;
val[r]=std::max(val[r],a);
}
val[l>>1]=std::max(val[l>>1],std::min(val[l],val[l^1]));
val[r>>1]=std::max(val[r>>1],std::min(val[r],val[r^1]));
}
for(;s_l>1;s_l>>=1){
val[s_l>>1]=std::max(val[s_l>>1],std::min(val[s_l],val[s_l^1]));
}
for(;s_r>1;s_r>>=1){
val[s_r>>1]=std::max(val[s_r>>1],std::min(val[s_r],val[s_r^1]));
}
}
int query(){
int ans=Inf;
for(int l=n,r=n*2;l>=1,r>>=1){
if(l&1){
ans=std::min(ans,val[l++]);
}
if(r&1){
ans=std::min(ans,val[--r]);
}
}
return ans;
}
}seg[Maxb<<1|5];
int seg_id[Maxn+5];
int seg_id_tot;
void init_dfs(int u){
if(vis_down[u]==0){
return;
}
if(vis[u]&&fa[u]){
seg_id[u]=++seg_id_tot;
seg[seg_id[u]].init(dep[u]-dep[vis_up[fa[u]]]);
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
init_dfs(v);
}
}
void init(){
seg_id_tot=0;
init_dfs(1);
}
void modify_path(int u,int v,int val){
if(dep[u]>dep[v]){
std::swap(u,v);
}
int pos=vis_down[v];
seg[seg_id[pos]].update(dep[u]-dep[vis_up[fa[pos]]]+1,dep[v]-dep[vis_up[fa[pos]]],val);
}
int query_path(int u){
return seg[seg_id[vis_down[u]]].query();
}
}
namespace Subtask_3{
struct Node{
int ch[2];
int dep_l,dep_r;
int cur_val,lazy;
}node[Maxb<<1|5];
int sz[Maxb<<1|5],fa[Maxb<<1|5],dep[Maxb<<1|5],son[Maxb<<1|5],top[Maxb<<1|5],mn_r[Maxb<<1|5];
std::vector g[Maxb<<1|5];
int re_id[Maxn+5],re_id_tot;
void init_dfs_1(int u){
dep[u]=dep[fa[u]]+1;
sz[u]=1;
son[u]=0;
for(int v:g[u]){
fa[v]=u;
init_dfs_1(v);
sz[u]+=sz[v];
if(son[u]==0||sz[v]>sz[son[u]]){
son[u]=v;
}
}
}
int lis[Maxb<<1|5],len;
int sum_sz[Maxb<<1|5];
int build_line(int l,int r){
if(l>r){
return 0;
}
int left=l,right=r;
while(leftnode[root].dep_r||val<=node[root].lazy){
return;
}
if(l<=node[root].dep_l&&r>=node[root].dep_r){
update_tag(root,val);
return;
}
if(l<=dep[root]&&r>=dep[root]){
node[root].cur_val=std::max(node[root].cur_val,val);
}
update(node[root].ch[0],l,r,val),update(node[root].ch[1],l,r,val);
}
void modify_path(int u,int v,int val){
int num=0;
u=re_id[u],v=re_id[v];
while(top[u]!=top[v]){
if(dep[top[u]]dep[v]){
std::swap(u,v);
}
num++;
update(mn_r[u],dep[u]+1,dep[v],val);
// fprintf(stderr,"%d %d\n",re_id_tot,num);
}
void _push_down(int root,int val=0){
if(root==0){
return;
}
val=std::max(val,node[root].lazy);
node[root].lazy=val;
node[root].cur_val=std::max(node[root].cur_val,val);
_push_down(node[root].ch[0],val),_push_down(node[root].ch[1],val);
}
void push_down(){
for(int i=1;i<=re_id_tot;i++){
if(top[i]==i){
_push_down(mn_r[i]);
}
}
}
void init(){
for(int i=1;i<=re_id_tot;i++){
fa[i]=0,g[i].clear();
}
re_id_tot=0;
for(int i=1;i<=n;i++){
if(vis[i]){
re_id[i]=++re_id_tot;
}
}
for(int i=1;i<=n;i++){
if(vis[i]){
if(::fa[i]){
g[re_id[vis_up[::fa[i]]]].push_back(re_id[i]);
}
}
}
init_dfs_1(1);
init_dfs_2(1,1);
}
namespace DSU{
int num;
int fa[Maxb<<1|5];
void init(){
num=re_id_tot;
for(int i=1;i<=re_id_tot;i++){
fa[i]=i;
}
}
int find(int x){
if(x==fa[x]){
return x;
}
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fa_x=find(x),fa_y=find(y);
if(fa_x==fa_y){
return;
}
fa[fa_y]=fa_x;
num--;
}
}
void merge_path(int u,int v){
while(u!=v){
if(dep[u]=l;i--){
if(!vis_op[i]){
continue;
}
int cur_u=re_id[edge[i].u],cur_v=re_id[edge[i].v];
merge_path(cur_u,cur_v);
if(DSU::num==1){
return i;
}
}
push_down();
int ans=Inf;
for(int i=2;i<=re_id_tot;i++){
if(DSU::find(i)==i){
ans=std::min(ans,node[i].cur_val);
}
}
return ans;
}
}
void change_path(int u,int v,int val){
if(u==v){
return;
}
Subtask_2::modify_path(u,v,val);
Subtask_3::modify_path(vis_up[fa[v]],vis_down[v],Subtask_2::query_path(v));
}
void _update_path(int u,int fa,int val){
if(u==fa){
return;
}
if(!on_path[fa]){
Subtask_1::modify_path(u,fa,val);
}
else if(on_path[u]){
if(vis_up[::fa[u]]==vis_up[fa]){
change_path(fa,u,val);
}
else{
change_path(fa,vis_down[fa],val);
change_path(vis_up[::fa[u]],u,val);
Subtask_3::modify_path(vis_down[fa],vis_up[::fa[u]],val);
}
}
else{
Subtask_1::modify_root(u,val);
u=vis_up[u];
_update_path(u,fa,val);
}
}
void update_path(int u,int v,int val){
int lca=find_lca(u,v);
_update_path(u,lca,val),_update_path(v,lca,val);
}
int query(int bel_l,int r){
return std::min(Subtask_1::val_pos,Subtask_3::query(bel_l,r));
}
namespace Init{
int head[Maxn+5],arrive[Maxn<<1|5],nxt[Maxn<<1|5],tot;
void add_edge(int from,int to){
arrive[++tot]=to;
nxt[tot]=head[from];
head[from]=tot;
}
int dfn[Maxn+5],dfn_tot;
void init_dfs(int u,int fa){
dfn[u]=++dfn_tot;
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa){
continue;
}
init_dfs(v,u);
::add_edge(dfn[u],dfn[v]);
}
}
void init(){
for(int i=1;iMaxb){
break;
}
}
vir_dfs_1(1),vir_dfs_2(1);
Subtask_1::init();
Subtask_2::init();
Subtask_3::init();
std::vector cur_q;
for(int j=1;j<=q;j++){
if(op_id[j]<0){
std::sort(cur_q.begin(),cur_q.end(),[](int p,int q){return p>q;});
for(int k:cur_q){
update_path(edge[k].u,edge[k].v,k);
}
cur_q.clear();
if(l<=-op_id[j]&&-op_id[j]<=r){
ans[j]=query(l,-op_id[j]);
}
}
else{
if(op_id[j]
\(T2\ zero\)
我们可以推出来第一步
考虑一下生成函数
\[F_k=F_{k-1}+x F_k + x^2F_k \\ (1-x-x^2)F_k=F_{k-1} \\ F_k=\frac{F_{k-1}}{1-x-x^2} \\ F_k=\frac{F_1}{(1-x-x^2)^{k-1}} \\ F_k=\frac{1}{(1-x-x^2)^{k}} \]\(F_0\)是原始的斐波那契数列
我们要求\(F_k\)的第\(n\)项,\(n<=1\times 10^{100000}\)
这个暴力多项式计算的话,会寄,可以拿到\(1pts\)
这个常系数线性齐次递推的话,会寄,可以拿到\(1pts\)
这个暴力递推的话,不会寄。。。
考虑继续推式子
\[[x^n]\frac{1}{(1-x-x^2)^k} \\ \lambda_1=\frac{1+\sqrt 5}{2}\ \ \ \ \ \lambda_2=\frac{1-\sqrt 5}{2} \\ [x^n]\frac{1}{(1-\lambda_1 x)^k}\frac{1}{(1-\lambda_2 x)^k} \]考虑初始形态
\[\frac{1}{1-x}=1+x+x^2+x^3... \\ [x^n]\frac{1}{(1-x)^k}=\binom{n+k-1}{k-1} \]首先设
\[K=k-1 \\ q=\frac{\lambda_1}{\lambda_2} \\ C=\frac{\lambda_2^n (-1)^K}{(K!)^2} \]然后
\[\sum_{i=0}^n\binom{i+K}{K}\binom{n-i+K}{K}\lambda_1^i\lambda_2^{n-i} \\ \frac{\lambda_2^n}{(K!)^2}\sum_{i=0}^n(i+K)^{\underline{K}}(n-i+K)^{\underline{K}}q^i \\ C\sum_{i=0}^n (i+K)^{\underline{K}}(i-n-1)^{\underline{K}}q^i \\ C\sum_{i=0}^n(i+K)^{\underline{K}}q^i\sum_{j=0}^K\binom{K}{j} i^j(-n-1)^{K-j} \\ C\sum_{j=0}^K\binom{K}{j}(-n-1)^{\underline{K-j}}\sum_{i=0}^n(i+K)^{\underline{K+j}}q^i \]前半部分可以做到\(O(k)\),考虑后半部分如何线性预处理
\[S_p=\sum_{i=0}^n(i+K)^{\underline{p}}q^i \\ \sum_{i=0}^n (i+K-1)^{\underline{p}}q^i+p(i+K-1)^{\underline{p-1}}q^i \\ q\times S_p-q^{n+1}(n+K)^{\underline{p}}+(K-1)^\underline{p}+p(q\times S_{p-1}-q^{n+1}(n+K)^{\underline{p-1}}+(K-1)^\underline{p-1}) \]也可以\(O(k)\)得到
#include
#define N 10000000
#define int long long
#define mode 469762049
using namespace std;
int poww(int x,int y)
{
int su=1;
while(y)
{
if(y&1)
{
su=su*x%mode;
}
x=x*x%mode;
y>>=1;
}
return su;
}
const int lam1=poww(6922763,mode-2),lam2=poww(462839285,mode-2),q=lam1*poww(lam2,mode-2)%mode,inq=poww(q,mode-2);
int inv(int x)
{
return poww(x,mode-2);
}
int k,n,nn,jc[N+10],invs[N+10],f[N*2+10];
char s[N];
signed main()
{
jc[0]=1;
for(int i=1;i<=N;++i) jc[i]=jc[i-1]*i%mode;
invs[N]=inv(jc[N]);
for(int i=N-1;i>=0;--i) invs[i]=invs[i+1]*(i+1)%mode;
cin>>k>>s+1;
int l=strlen(s+1);
for(int i=1;i<=l;++i)
{
n=(n*10+s[i]-'0')%mode;
nn=(nn*10+s[i]-'0')%(mode-1);
}
int f1=mode-poww(q,nn+1),f2=1,v1=(n+k-1)%mode,v2=(k-2+mode)%mode,iv=inv(mode+1-q);
f[0]=iv*(f1+1)%mode;
for(int i=1;i<=2*k;++i)
{
f[i]=i*((q*f[i-1]%mode+f1+f2)%mode)%mode;
f1=f1*(v1--)%mode;
f2=f2*(v2--)%mode;
f[i]=(f[i]+f1+f2)*iv%mode;
}
int cf=poww(lam2,nn)*invs[k-1]%mode*invs[k-1]%mode;
if(k%2==0) cf=(mode-cf)%mode;
int ans=0,v=mode-n-1,ff=1;
for(int i=k-1;i>=0;--i)
{
int p=jc[k-1]*invs[i]%mode*invs[k-1-i]%mode*ff%mode;
ans=(ans+p*f[k-1+i]%mode)%mode;
ff=ff*(v--)%mode;
}
ans=ans*cf%mode;
cout<
\(T3\ diana\)
首先容易想到式子
\[\varphi(a\times b)=\frac{\varphi(a)\varphi(b)\gcd(a,b)}{\varphi(\gcd(a,b))} \]维护区间最大值,由于后面的\(\frac{\gcd(a,b)}{\varphi(gcd(a,b))}\)不好处理,那么就枚举这一部分
我们枚举\(\gcd\),拿出所有\(\gcd\)的倍数,按照\(\frac{\varphi(a)\varphi(b)\varphi(\gcd)}{\gcd}\)去计算,这样算即使部分不是正确的,但算出的结果会\(\leq\)真实结果
我们可以暴力枚举\(\gcd\),暴力统计,复杂度\(O(q\times n\times \ln (n))\)
考虑 \(q\) 很大而 \(n\) 很小,我们枚举 \(\gcd\) 记三元组 \((i,j,val)\),然后扫描线一下即可
这个时候的问题是,\(n\) 大的时候,三元组个数会很多
当 \(\gcd\) 确定时大小只和 \(\varphi(p_i)\) 和 \(\varphi(p_j)\) 有关,考虑枚举两者之间的较小值
结论:答案只可能是距离 \(i\) 最近的并且 \(\varphi(p_j)>\varphi(p_i)\) 的左右两值
证明:考虑\(\varphi(p_k)>\varphi(p_j)>\varphi(p_i),k
然后现在三元组数量控制在 \(O(n\ln n)\)就可以愉快扫描线了
\(45pts\)线段树惨遭卡常代码
#include
#define int long long
#define MAXN 500005
using namespace std;
struct node
{
int l,r,id,val;
}li[MAXN<<5];
int Ans[MAXN];
template
T Read()
{
T x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int (*read)()=Read;
#define read Read
namespace Seg
{
#define ls (now<<1)
#define rs ((now<<1)|1)
struct Tree
{
int l,r,Max;
}tr[MAXN<<4];
void build(int now,int l,int r)
{
tr[now].l=l;
tr[now].r=r;
if(l==r) return ;
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
void push_up(int now)
{
tr[now].Max=max(tr[ls].Max,tr[rs].Max);
}
void change(int now,int poz,int k)
{
if(tr[now].l==poz&&tr[now].r==poz)
{
tr[now].Max=max(tr[now].Max,k);
return;
}
int mid=(tr[now].l+tr[now].r)>>1;
if(poz<=mid) change(ls,poz,k);
else change(rs,poz,k);
push_up(now);
}
int query(int now,int l,int r)
{
if(tr[now].l>=l&&tr[now].r<=r)
{
return tr[now].Max;
}
int mid=(tr[now].l+tr[now].r)>>1;
int res=0;
if(l<=mid) res=max(res,query(ls,l,r));
if(r>mid) res=max(res,query(rs,l,r));
return res;
}
}
bool vis[MAXN+5];
int pri[MAXN+5],phi[MAXN+5],sta[MAXN],poz[MAXN],p[MAXN],num,cnt,q,n;
void Init()
{
phi[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(!vis[i])
{
pri[++num]=i;
phi[i]=i-1;
}
for(int j=1;j<=num&&i*pri[j]<=MAXN;j++)
{
int now=i*pri[j];
vis[now]=true;
if(i%pri[j]==0)
{
phi[now]=phi[i]*pri[j];
break;
}
phi[now]=phi[i]*(pri[j]-1);
}
}
}
inline void Print(int x){
static char tmp[250];
if(x == 0) putchar('0');
int t = 0;
while(x) tmp[++t] = x % 10 + '0' , x /= 10;
while(t) putchar(tmp[t]) , t--;
putchar('\n');
}
signed main()
{
Init();
cin>>n;
for(int i=1;i<=n;i++)
{
p[i]=read();
poz[p[i]]=i;
}
// cout<<"line:\n";
// for(int i=1;i<=n;i++) cout< >Mid;
for(int i=G;i<=n;i+=G)
{
Mid.push_back(make_pair(poz[i],phi[i]));
}
sort(Mid.begin(),Mid.end());
int top=0;
for(int i=0;i=0;i--)
{
while(top&&Mid[sta[top]].second<=Mid[i].second)
{
++cnt;
// cout<
不得不写成树状数组
/*
如果写线段树的话,建议写zkw
亲身验证普通线段树只能45pts
*/
#include
#define int long long
#define MAXN 500005
using namespace std;
struct node
{
int l,r,id,val;
}li[MAXN<<5];
int Ans[MAXN];
template
T Read()
{
T x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int (*read)()=Read;
#define read Read
bool vis[MAXN+5];
int pri[MAXN+5],phi[MAXN+5],sta[MAXN],poz[MAXN],p[MAXN],num,cnt,q,n;
namespace Seg
{
int tr[MAXN];
int lowbit(int x)
{
return x&(-x);
}
void Insert(int x,int k)
{
while(x) tr[x]=max(tr[x],k),x-=lowbit(x);
}
int Query(int x)
{
int res=0;
while(x<=n) res=max(res,tr[x]),x+=lowbit(x);
return res;
}
// #define ls (now<<1)
// #define rs ((now<<1)|1)
// struct Tree
// {
// int l,r,Max;
// }tr[MAXN<<4];
// void build(int now,int l,int r)
// {
// tr[now].l=l;
// tr[now].r=r;
// if(l==r) return ;
// int mid=(l+r)>>1;
// build(ls,l,mid);
// build(rs,mid+1,r);
// }
// void push_up(int now)
// {
// tr[now].Max=max(tr[ls].Max,tr[rs].Max);
// }
// void change(int now,int poz,int k)
// {
// if(tr[now].l==poz&&tr[now].r==poz)
// {
// tr[now].Max=max(tr[now].Max,k);
// return;
// }
// int mid=(tr[now].l+tr[now].r)>>1;
// if(poz<=mid) change(ls,poz,k);
// else change(rs,poz,k);
// push_up(now);
// }
// int query(int now,int l,int r)
// {
// if(tr[now].l>=l&&tr[now].r<=r)
// {
// return tr[now].Max;
// }
// int mid=(tr[now].l+tr[now].r)>>1;
// int res=0;
// if(l<=mid) res=max(res,query(ls,l,r));
// if(r>mid) res=max(res,query(rs,l,r));
// return res;
// }
}
void Init()
{
phi[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(!vis[i])
{
pri[++num]=i;
phi[i]=i-1;
}
for(int j=1;j<=num&&i*pri[j]<=MAXN;j++)
{
int now=i*pri[j];
vis[now]=true;
if(i%pri[j]==0)
{
phi[now]=phi[i]*pri[j];
break;
}
phi[now]=phi[i]*(pri[j]-1);
}
}
}
inline void Print(int x)
{
static char tmp[250];
if(x == 0) putchar('0');
int t = 0;
while(x) tmp[++t] = x % 10 + '0' , x /= 10;
while(t) putchar(tmp[t]) , t--;
putchar('\n');
}
signed main()
{
Init();
cin>>n;
for(int i=1;i<=n;i++)
{
p[i]=read();
poz[p[i]]=i;
}
// cout<<"line:\n";
// for(int i=1;i<=n;i++) cout< >Mid;
for(int i=G;i<=n;i+=G)
{
Mid.push_back(make_pair(poz[i],phi[i]));
}
sort(Mid.begin(),Mid.end());
int top=0;
for(int i=0;i=0;i--)
{
while(top&&Mid[sta[top]].second<=Mid[i].second)
{
++cnt;
// cout<