2020牛客暑期多校训练营(第一场)——B Infinite Tree
输入
3
1 1 1
4
3 1 2 4
4
0 0 0 0
输出
3
17
0
题目大意
题解
首先,计算{1!, 2!, ..., n!}的“virual tree”;其次,要计算实际成本,请使用Segment Tree或Fenwick Tree.。(官方题解)
事实上大家基本使用“虚树”来求解这题(要将整棵树全部保存下来是不可能的),可以通过相邻节点的lca 建树。
我们先设数i,而i!分解质因数后会是这样的形式:p1 x1 p2 x2 p3 x3...pn xn(p1<p2<p3...<pn)
而我们的(i+1)!,在上面式子的基础上,会增加一些pj上的x_jxj的幂次,或者加入新的pn+1。
我们设从右往左第一个幂次不等的质因数为pi,则在pi^xi走完以后,i!会走pi−1,而(i+1)!会继续走pi,使(i+1)!的dfs序变大。
当(i+1)是一个质数时,显然第一个幂次不等的质因子就是新的pn+1。在这种情况下dfs序会更小;
综上,在递归时,对于任意的 i∈[1,n),dfn[(i+1)!]>dfn[i!]恒成立。
所以,可以得出dep[i!]=∑xi
稍加思索,我们在这里要求的可以表示为dep[lca(i!,(i+1)!)],而i!和(i+1)!的lca,就是pi pi+1...pn。
当i+1i+1是一个质数时,他们的lca为1构建虚树的过程,就是利用栈维护一条链,出现新的lca,而这个lca不会再和其它点再求lca。因此,只需要处理出i!和(i+1)!的lca的深度即可。
AC Code
#include<bits/stdc++.h>
using namespace std;
#define N 100005
vector<int>v[N];//Infinite Tree
int n,vis[N],sum[N],le[N],ri[N];
long long s,ss,ans,a[N];
int main()
{
for(int i=2;i<N-4;i++)
if(!vis[i])
{
sum[i]=1;v[i].push_back(i);
for(int j=2;i*j<N-4;j++)
{
vis[i*j]=1;
for(int k=i*j;k%i==0;k/=i)
sum[i*j]++,v[i].push_back(i*j);
}
}//预处理
for(int i=2;i<N-4;i++)sum[i]+=sum[i-1];
while (scanf("%d",&n)!=EOF)
{
s=ans=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
s+=a[i];
ans+=a[i]*sum[i];
a[i]+=a[i-1];
}
int l=1,r=n,las=0;
for(int i=n;i>=2;i--)
{
if(vis[i])continue;
for(int j=0;(j<v[i].size())&&(v[i][j]<=r);j++)
{
if(l<v[i][j])
{
ss=2*(a[v[i][j]-1]-a[l-1]);
if(s-ss<=0)
{
s-=2*(a[r]-a[v[i][j]-1]);
r=v[i][j]-1;
break;
}
s-=ss;l=v[i][j];
}
ans-=s;
}
if(s<=0)break;
}
printf("%lld\n",ans);
}
}