题解 P7091 【数上的树】
- Update on 2023.4.4 勘误:
g_i 实际上等于d_i 质因子数量的两倍减去1 。
题目传送门。
首先将
设
显然,局部最优值可以保证整体最优值,且转移无后效性,即求
对于不能出现在树上的
设
假设当前转移
- Case 1:
d_j 和d_k 子树内各一个节点组合出的 pairs。因为它们的 LCA 是d_i ,且共有g_j\times g_k 对 pairs,故贡献为g_j\times g_k\times d_i 。 - Case 2:
d_i 和任意一个节点组合出的 pairs。显然贡献为g_i\times d_i 。
转移方程:
其中
这样子搞是
- 剪枝 1:在枚举内层循环
j 时发现k 有单调性,所以直接用指针代替k 即可。这样时间复杂度降为了\mathcal O(c^2) 。 - 剪枝 2:当
j>k 时直接 break,减小常数。
综上,我们有了一个
ll n,m,num[N],f[N];
ll cnt,pr[N],c[N],tot;
ll fc[N],il[N],d;
map <ll,int> isp;
void dfs(int pos,ll prod){
if(pos>cnt){
if(prod>1)fc[++d]=prod;
return;
} for(int i=0;i<=c[pos];i++)dfs(pos+1,prod),prod*=pr[pos];
}
int main(){
cin>>n>>m;
// factor
ll tmp=n;
for(ll i=2;i*i<=n;i++)
if(n%i==0){
pr[++cnt]=i,isp[i]=1;
while(n%i==0)c[cnt]++,tot++,n/=i;
}
if(n>1)pr[++cnt]=n,tot++,c[cnt]=1,isp[n]=1;
n=tmp;
// find factors
dfs(1,1);
sort(fc+1,fc+d+1);
// limit
for(int i=1;i<=m;i++){
ll val=read();
int pos=lower_bound(fc+1,fc+d+1,val)-fc;
if(pos<=d&&fc[pos]==val)il[pos]=1; // 表示 pos 不能出现
}
// dp
for(int i=1;i<=d;i++){
if(il[i])continue;
if(isp[fc[i]]){
num[i]=1,f[i]=fc[i];
continue;
} il[i]=1,f[i]=inf; // 如果无法由以前的 j,k 转移得到那么 i 也无法得到
int p=i-1;
for(int j=1;j<i;j++){
if(fc[i]%fc[j])continue;
while(fc[p]>fc[i]/fc[j])p--;
if(j>p)break;
if(!il[j]&&!il[p])f[i]=min(f[i],f[j]+f[p]+num[j]*num[p]*fc[i]+(num[i]=num[j]+num[p]+1)*fc[i]),il[i]=0;
}
} if(il[d])puts("-1");
else cout<<(ll)f[d]<<endl;
return 0;
}