[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;
}
posted @ 2026-03-25 09:54  Oken喵~  阅读(11)  评论(0)    收藏  举报