第一场真的怀疑人生 做题做不动 补题补不动 还有叉姐的一句话题解 (看不懂啊呜呜呜)
然后我左翻右翻 终于找到了这题的题解 博主讲的非常详细 : Infinite Tree
知道有虚树这个东西 但是还没看过 虚树的建树过程大概就是 先把所有需要的点拿出来 显然这题我们需要的点 就是 1!,2!,3!,,,,,,,n! 这n个点 然后把他们按dfs序排序 所幸 这题按照1~n的顺序就是 dfs序排序的 我们找出每个点前面那个点的lca 最后套一个虚树的板子就可以建树了
这些议事点是好处理的 我们画图就知道 i点的深度就是 i!包含的质因数的个数 (1!的深度为0) 我们用增量法可以完成建树 例如 2!建完后我们建立3! 显然再2!的基础上 多了3这个因子 那么我们把多的这个数进行质因数分解就可以 因此3!的深度就是 2!的深度+1
关键是lca的深度怎么处理? 我们观察5!和6! 显然 他们分叉的地方在6的最大质因数的地方 那么5!和6!的lca的深度我们已经可以猜测出来了 其实就是 dep[lca(5!,6!)]=sum(6的最大质因数,n); 也就是说有全面所有大于等于6的最大质因数的 这些都会在lca到根节点的这条链上 之后就会开始分岔 计数可以用树状数组 可以看上面那个博主的图 多画一下就能理解
树建成以后考虑怎么去计算答案 首先我们统计以1为u的答案 显然就是 所有的 dp[i]*w[i] (第i个点代表 i! 这个点)
但是这不一定是最优答案 因为我们可以以其他的点为u 而其他点可以为u的充分必要条件是 w[1]-2*w[v]<0
我们假设 1结点为u 而我们已经算出一个答案ans
遍历1的所有子节点v 如果我们把u转移到v 那么处于1那边点到达v的距离会增加 dep[v]-dep[1] 处于v这一侧的点 的距离会减少 dep[v]-dep[1] (类似换根dp) 而增加的总权值是 w[1]-w[v], 减少的总权值是 w[v] 所以 如果 w[1]-2*w[v]<0的话 我们会转移u结点 并且更新答案 因为 w[1]/2<w[v] 可知 满足这个条件的路径是唯一的 因此这样是可行的
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+100;
int c[N],n,tot,top,w[N],dep[N],lcadep[N],st[N];
typedef long long ll;
ll ans;
vector<int>g[N];
int mindiv[N];
void init(){
ans=top=0;
for(int i = 1; i <= tot; i++)
g[i].clear(),c[i]=w[i]=dep[i]=lcadep[i]=0;
}
void add(int x,int val){
while(x<=n){
c[x]+=val;
x+=x&-x;
}
}
int query(int x){
int ret=0;
while(x){
ret+=c[x];
x-=x&-x;
}
return ret;
}
void add_edge(int x,int y){
g[x].push_back(y);g[y].push_back(x);
}
void sieve(int mx){
for(int i = 2; i <= mx; i++){
if(!mindiv[i])
for(int j = i; j <= mx; j+=i)
if(!mindiv[j]) mindiv[j]=i;
}
}
void build(){
st[top=1]=1;tot=n;
for(int i = 2; i <= n; i++){
dep[i]=dep[i-1]+1;int j = i;
for( ; j != mindiv[j]; j /= mindiv[j]) dep[i]++;
lcadep[i] = query(n)-query(j-1);
for(j = i; j != 1; j /= mindiv[j]) add(mindiv[j],1);
}
for(int i = 2; i <= n; i++){
while(top>1&&dep[st[top-1]]>=lcadep[i])
add_edge(st[top-1],st[top]),top--;
if(dep[st[top]]!=lcadep[i]){
dep[++tot]=lcadep[i];
add_edge(tot,st[top]);
st[top]=tot;
}
st[++top]=i;
}
while(top>1) add_edge(st[top-1],st[top]),top--;
}
void dfs1(int u,int f){
ans+=1ll*dep[u]*w[u];
for(auto v:g[u]){
if(v!=f){
dfs1(v,u);
w[u]+=w[v];
}
}
}
void dfs2(int u,int f){
for(auto v:g[u]){
if(v!=f){
if(w[1]-2*w[v]<0){
ans+=1ll*(w[1]-2*w[v])*(dep[v]-dep[u]);
dfs2(v,u);
}
}
}
}
int main(){
sieve(1e5);
while(~scanf("%d",&n)){
init();
for(int i = 1; i <= n; i++) scanf("%d",&w[i]);
build();
dfs1(1,0);dfs2(1,0);
printf("%lld\n",ans);
}
return 0;
}