第一道独立完成的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; }
来源:https://www.cnblogs.com/mlystdcall/p/12315580.html