小白月赛最后一题,树形dp有两种方式做,第一种思维要求高一点,第二种代码麻烦一点。
解法一: 从树形结构自下而上观察本问题(上指的是树根,下指的是叶子)。 某个节点,与其子节点有公共的最大公因数,两个节点中必须要有一个要删除他们的最大公因数,删那个的对结果的贡献都是最大公因数的质数组成数。那么我们删哪一个呢?我们删父节点! 因为 删了父节点之后,因为我们是从叶子节点看起,删了父节点,这个gcd的影响就传不到父节点的父节点去了,而且还防止了删了这个子节点的gcd,别的子节点和父节点还有gcd或者这个gcd的因子 所以从叶子节点往上,我们只需要一直删父节点的gcd就行了,并且将删gcd的次数加到答案里
代码如下:十分优美
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
# define rep(i,be,en) for(int i=be;i<=en;i++)
# define pre(i,be,en) for(int i=be;i>=en;i--)
#define ll long long
#define endl "\n"
#define LOCAL
#define pb push_back
#define int long long
typedef pair<ll, ll> PII;
#define eb emplace_back
#define sp(i) setprecision(i)
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
int cnt[N];
int w[N];
vector<int>g[N];
int n;
int ans=0;
void init()
{
for(int i=2;i<N;i++)
{
if(!cnt[i])
{
for(int j=i;j<N;j+=i)
{
int t=j;
while(t%i==0) cnt[j]++,t/=i;
}
}
}
}
void dfs(int u,int fa)
{
for(auto x: g[u])
{
if(x==fa) continue;
dfs(x,u);
ans+=cnt[__gcd(w[u],w[x])];
w[u]/=__gcd(w[u],w[x]);
}
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<=n-1;i++)
{
int x,y;
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1,-1);
cout<<ans<<endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int __ = 1;init();
while (__--)
{
solve();
}
return 0;
}
**解法二:**维护每个最大的质因子组成的森林,用基础的区间dp来维护答案,我们用
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]表示没有选i子树删了目前的质因子的最小次数,
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]表示选了i子树删了目前质因子的最小次数。
状态转移为:
d
p
[
u
]
[
1
]
=
dp[u][1]=
dp[u][1]=
∑
m
i
n
(
d
p
[
x
]
[
1
]
,
d
p
[
x
]
[
0
]
)
+
c
n
t
\sum min(dp[x][1],dp[x][0])+cnt
∑min(dp[x][1],dp[x][0])+cnt
d
p
[
u
]
[
0
]
=
dp[u][0]=
dp[u][0]=
∑
d
p
[
x
]
[
1
]
\sum dp[x][1]
∑dp[x][1]
代码如下:详情看注释
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
# define rep(i,be,en) for(int i=be;i<=en;i++)
# define pre(i,be,en) for(int i=be;i>=en;i--)
#define ll long long
#define endl "\n"
#define LOCAL
#define pb push_back
#define int long long
typedef pair<ll, ll> PII;
#define eb emplace_back
#define sp(i) setprecision(i)
const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int w[N];
int n;
int prime[N];
bool st[N];
int f[N][2];
int maxp[N];
int cnt = 0;
vector<int>g[N];
vector<int>rk[N];
void init()
{
for (int i = 2; i < N; i++)
{
if (!maxp[i]) prime[cnt++] = i, maxp[i] = i;
for (int j = 0; j < cnt && prime[j]*i < N; j++)
{
maxp[i * prime[j]] = prime[j];
if (i % prime[j] == 0) break;
}
}
}
void dfs(int u, int fa, int p)
{
st[u] = 1;
f[u][0] = 0;
int t = w[u], c = 0;
while (t % p == 0)
{
c++;
t /= p;
}
f[u][1] = c;
for (auto x : g[u])
{
if (w[x] % p != 0 || x == fa || st[x]) continue;
dfs(x, u, p);
f[u][0] += f[x][1];
f[u][1] += min(f[x][1], f[x][0]);
}
}
void solve()
{
cin >> n;
init();
for (int i = 1; i <= n; i++)
{
cin >> w[i];
int t = w[i];
while (t > 1)
{
rk[maxp[t]].push_back(i);
t /= maxp[t];
}
}
for (int i = 1; i < n; i++)
{
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
int ans = 0;
for (int i = 0; i < cnt; i++)
{
for (auto x : rk[prime[i]])
{
st[x] = 0;
f[x][0] = 0;
f[x][1] = 0;
}
for (auto x : rk[prime[i]])
{
if (!st[x])
{
dfs(x, -1, prime[i]);
ans += min(f[x][0], f[x][1]);
}
}
}
cout << ans << endl;
}
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int __ = 1;
while (__--)
{
solve();
}
return 0;
}
|