[THUPC 2018] 城市地铁规划
[THUPC 2018] 城市地铁规划
Prufer 序列练习题。
考虑对 Prufer 序列进行 dp,一个点的出现次数是其度数减 \(1\)。
我们定义 \(g(x) = f(x) \bmod 59393\),\(h(x) = g(x) - g(1)\)。
接下来我们定义 \(dp_i\) 表示 Prufer 序列填入前 \(i\) 位的最大价值,则有:
\[dp_i = \max\limits_{j=0}^{i-1} dp_j + h(i-j+1)
\]
注意我们这里钦定了同一个点在 Prufer 序列中的出现是连续的,这一步是有道理的。
同时我们需要注意 \(dp_0 = n \times g(1)\),使没有出现在 Prufer 序列内的点被统计到。
我们记录转移,显然可以还原出一个 Prufer 序列。
接下来由 Prufer 序列转成树的过程是简单的,时间复杂度 \(O(n^2)\)。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const long long mod=59393,INF=-0x3f3f3f3f3f3f3f3f;
long long a[20];
long long g[3010],h[3010],dp[3010],trans[3010];
int prufer[3010],deg[3010];
int main(){
int n,k;
scanf("%d %d",&n,&k);
for(int i=0;i<=k;i++){
scanf("%lld",&a[i]);
}
for(long long x=1;x<n;x++){
for(int i=k;i>=0;i--){
g[x]=(g[x]*x+a[i])%mod;
}
h[x]=g[x]-g[1];
}
dp[0]=g[1]*n;
for(int i=1;i<=n-2;i++){
dp[i]=INF;
for(int j=0;j<i;j++){
long long pre=dp[j]+h[i-j+1];
if(pre>dp[i]){
dp[i]=pre;
trans[i]=j;
}
}
}
int pre_num=0,pre_pos=n-2;
while(pre_pos){
int pre_lst=trans[pre_pos];
pre_num++;
for(int i=pre_pos;i>pre_lst;i--){
prufer[i]=pre_num;
}
pre_pos=pre_lst;
}
printf("%d %lld\n",n-1,dp[n-2]);
for(int i=1;i<=n;i++){
deg[i]=1;
}
for(int i=1;i<=n-2;i++){
deg[prufer[i]]++;
}
priority_queue<int,vector<int>,greater<int> > pq;
for(int i=1;i<=n;i++){
if(deg[i]==1){
pq.push(i);
}
}
for(int i=1;i<=n-2;i++){
int u=pq.top();
pq.pop();
printf("%d %d\n",prufer[i],u);
deg[prufer[i]]--;
deg[u]--;
if(deg[prufer[i]]==1){
pq.push(prufer[i]);
}
}
int u=pq.top();
pq.pop();
int v=pq.top();
pq.pop();
printf("%d %d",u,v);
return 0;
}

浙公网安备 33010602011771号