2020牛客暑期多校训练营(第一场)——B Infinite Tree

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);
	}
}

猜你喜欢

转载自blog.csdn.net/cxkdad/article/details/107689829