CF1778F Maximizing Root - 树形 dp -
题目链接:https://codeforces.com/problemset/problem/1778/F
题解:
设 \(dp_{i,j}\) 表示考虑到 \(i\) 结点,要让子树内的点都变成 \(a_i\) 第 \(j\) 小约数的倍数的话,至少要操作多少次
首先预处理一下 \(1..1000\) 的所有约数
考虑 \(x\) 每一次加一个子树 \(u\) 时的转移:
\(dp[x][i] += dp[u][j] + w(divs[a[x]][i], divs[a[u]][j])\)
其中 \(w\) 就是要考虑是否能通过操作 \(u\) 这个点使得 \(u\) 及子树的点能满足是 \(divs[a[x]][i]\) 的倍数
如果 \(divs[a[x]][i]|divs[a[u]][j]\) ,那么 \(w=0\)
否则,如果\(divs[a[x]][i]|divs[a[u]][j]^2\),则 \(w=1\)
否则,\(w=\infty\)
最后答案就是 \(a[1]\times divs[a[1]][i], if \ dp[1][i] < k\)
代码:
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back
using namespace std;
typedef long long ll;
typedef long long LL;
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5+5;
int n,k;
int a[maxn];
vector<int>g[maxn], divs[1005];
vector<int>dp[maxn];
void dfs(int x,int fat=0){
dp[x].clear();
dp[x].resize(divs[a[x]].size());
for(int u : g[x])if(u != fat){
dfs(u, x);
for(int j=0;j<divs[a[x]].size();j++){
int dj = divs[a[x]][j], r = k+1;
for(int kk=0;kk<divs[a[u]].size();kk++){
int dk = divs[a[u]][kk];
if(dk%dj == 0){
r = min(r, dp[u][kk]);
}else if(dk*dk%dj == 0){
r = min(r, dp[u][kk] + 1);
}
}
dp[x][j] = min(dp[x][j] + r, k+1);
}
}
dp[x][0] = 0;
}
void solve(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]), g[i].clear();
for(int i=1;i<n;i++){
int x,y;scanf("%d%d",&x,&y);
g[x].pb(y), g[y].pb(x);
}
dfs(1);
for(int i=dp[1].size()-1;i>=0;i--)
if(dp[1][i] < k){
printf("%lld\n",1ll*a[1]*divs[a[1]][i]);
return ;
}
printf("%d\n",a[1]);
}
signed main(){
for(int i=1;i<=1000;i++)
for(int j=i;j<=1000;j+=i)
divs[j].pb(i);
int an=0;
for(int i=1;i<=1000;i++)an=max(an,(int)divs[i].size());
cout << an;
int te;scanf("%d",&te);
while(te --)solve();
return 0;
}