【题解】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;
}
posted @ 2020-02-16 01:37  mlystdcall  阅读(286)  评论(0编辑  收藏  举报