【题解】Chaotic V. Codeforces 1292D 树上DP
第一道独立完成的Div1D
,嘿嘿
把树上的每个数字变一下
首先以1
为树根,假设一个点是u
,她的父亲是pa[u]
,那么把u
上面保存的数字变成u / pa[u]
这样的话,假设我们要找一个数字num
,可以先分解质因数,然后把质因数从大到小排序,从1
开始按顺序在树上走就可以了
如果暴力建树的话,节点个数会是1e7
这个数量级,因此把中间没用的部分压缩掉,节点个数就在[4e5, 5e5]
之间了,然后直接在树上DP
代码实现是一个难点,需要斟酌
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 500010;
const int M = 5010;
int _w;
int cnt[M], tot;
void read_cnt() {
int n;
_w = scanf( "%d", &n );
tot = n;
while( n-- ) {
int x;
_w = scanf( "%d", &x );
++cnt[x];
}
}
unordered_map<int,int> son[N];
int multi[N], mark[N], nid;
map<int,int> fac;
void factor( int x ) {
for( int i = 2; i*i <= x; ++i )
while( x % i == 0 ) {
++fac[i];
x /= i;
}
if( x != 1 ) ++fac[x];
}
void insert( int u, map<int,int>::reverse_iterator it, int cnt_mark ) {
if( it == fac.rend() ) {
mark[u] = cnt_mark;
return;
}
int num = it->first;
int c = it->second;
while( son[u].count(num) ) {
u = son[u][num];
c -= multi[u];
}
if( c ) {
int v = ++nid;
son[u][num] = v;
multi[v] = c;
insert(v, ++it, cnt_mark);
} else {
insert(u, ++it, cnt_mark);
}
}
void build_tree() {
nid = 1;
multi[1] = 1;
mark[1] = cnt[1];
for( int i = 2; i <= 5000; ++i ) {
factor(i);
insert(1, fac.rbegin(), cnt[i]);
}
// cout << nid << endl;
}
int sub[N];
ll f[N], ans;
void init_dfs( int u, int dep ) {
dep += multi[u];
ans += 1LL * dep * mark[u];
sub[u] = mark[u];
for( pii tmp : son[u] ) {
int v = tmp.second;
init_dfs(v, dep);
sub[u] += sub[v];
}
}
void init_dp() {
init_dfs(1, -1);
f[1] = ans;
}
void dp_dfs( int u ) {
int sub_cnt = sub[u];
int another = tot - sub_cnt;
int dis = multi[u] - 1;
ans = min( ans, f[u] );
ans = min( ans, f[u] - 1LL * dis * another + 1LL * dis * sub_cnt );
for( pii tmp : son[u] ) {
int v = tmp.second;
sub_cnt = sub[v];
another = tot - sub_cnt;
f[v] = f[u] - 1LL * multi[v] * sub_cnt + 1LL * multi[v] * another;
dp_dfs(v);
}
}
void dp() {
dp_dfs(1);
}
int main() {
read_cnt();
build_tree();
init_dp();
dp();
printf( "%lld\n", ans );
return 0;
}