思路
很明显 prufer 序列,度数为 (d_i) 的点在 prufer 序列中出现的次数为 (d_i - 1)。
代入 (n) 个值得出每个度数的价值,问题就是要背包出 (n - 2) 个点,求最大的价值。
可以 (mathcal O(n^2 log n)) 倍增 DP,但有更优的做法。
先假设序列中没有任何元素,则每个点都是叶子,答案是 (n cdot f(1)),然后 DP 一个点加入多少个,设某个点加入了 (i) 个,价值就是 (f(i) - f(1)),因为它不再是叶子了 ,费用是 (i)。
完全背包出 (n - 2) 个点最优方案,然后构造出序列,再构造树即可。
注意特判一下 (n = 1),因为不存在 prufer 序列。
Code
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define File(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
typedef long long ll;
template<typename T> inline void gi(T &x){
char ch; x = 0;
int f = 1;
while(isspace(ch = getchar()));
if(ch == '-') ch = getchar(), f = -1;
do x = x * 10 + ch - '0'; while(isdigit(ch = getchar()));
x *= f;
}
template<class T> void upmax(T &x, T y){x = x>y ? x : y;}
template<class T> void upmin(T &x, T y){x = x<y ? x : y;}
const int N = 5000, K = 15, mod = 59393;
int coef[K];
int n, k;
int getVal(int x){
int res = 0;
for(int i=k; i>=0; i--)
res = (res * x + coef[i]) % mod;
return res;
}
int f[N], tr[N], g[N];
int pru[N], pc = 0, idc = 0, cnt[N];
int main(){
int n;
gi(n); gi(k);
if(n == 1) {
int x; gi(x);
printf("0 %d
", x); return 0;
}
for(int i=0; i<=k; i++) gi(coef[i]);
for(int i=1; i<n; i++) g[i] = getVal(i);
for(int i=1; i<=n-2; i++)
for(int j=i; j<=n-2; j++){
if(f[j - i] + g[i + 1] - g[1] > f[j]){
f[j] = f[j - i] + g[i + 1] - g[1];
tr[j] = i;
}
}
priority_queue<int, vector<int>, greater<int> > S;
for(int i=n-2; i;){
++idc; cnt[idc] = tr[i];
for(int j=1; j<=tr[i]; j++) pru[++pc] = idc;
i -= tr[i];
}
for(int i=idc+1; i<=n; i++)
S.push(i);
printf("%d %d
", n - 1, f[n - 2] + g[1] * n);
for(int i=1; i<=n-2; i++){
int x = S.top(); S.pop();
printf("%d %d
", x, pru[i]);
--cnt[pru[i]];
if(cnt[pru[i]] == 0) S.push(pru[i]);
}
printf("%d ", S.top()); S.pop();
printf("%d
", S.top());
return 0;
}