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;
}
posted @ 2023-03-05 10:18  SkyRainWind  阅读(23)  评论(0编辑  收藏  举报