【20200203】【jwh】CF 1292C DP

怎么每次写题解都打不开CF啊qwq

题意

给一棵n个点的树,给树边加0~n-1的权,权值不重复

∑ u , v ∈ V , u ≠ v m e x ( p a t h u , v ) \sum_{u,v\in V,u\not=v}mex(path_{u,v}) u,vV,u=vmex(pathu,v)
的最大值
其中 p a t h u , v path_{u,v} pathu,v指以u,v为端点的路径上的边权组成的集合

思路

我首先考虑到了以下几点
(1)只有包含0的路径才对答案有贡献
(2)权值小的边就应该被包含于更多的路径中
本题的关键在于,每种可能的方案中一定存在一条路径,路径长为 l l l,恰好包含了0到l-1间的权值,并且使得 l l l最大
考虑到(2),我们发现,这条路径的最优情况一定是,路径的中间为0,向两侧递增的样子
于是使用dp去基于向短路径(比如l=k)两端中的一边加上权为k的边,来得到更长路径(l-k+1)的答案
然后就更新最大值就好了

代码

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef double db;
typedef complex<double> cp;
typedef pair<ll,ll> pll;

const int maxn=(int)3e3+9;
const int maxm=(int)1e6+9;
const ll mod=(ll)1e9+7;
const db pi=acos(-1);
const db eps=1e-15;

#define dbg(x) cerr<<#x<<" is "<<x<<endl;

vector<int> e[maxn];
ll f[maxn][maxn];
int n;
int fa[maxn][maxn];
int sz[maxn][maxn];

void init(){
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		a--,b--;
		e[a].push_back(b);
		e[b].push_back(a);
	}
}

void dfs(int u,int f,int r){
	fa[r][u]=f;
	sz[r][u]=1;
	for(int x:e[u]){
		if(f==x)continue;
		dfs(x,u,r);
		sz[r][u]+=sz[r][x];
	}
}

ll dp(int u,int v){
	if(u==v)return 0;
	if(f[u][v]>=0)return f[u][v];
	int fu=fa[v][u];
	int fv=fa[u][v];
	f[u][v]=max(dp(fu,v),dp(u,fv))+sz[v][u]*sz[u][v];
	f[v][u]=f[u][v];
	return f[u][v];
}

int main(){
	init();
	memset(f,-1,sizeof(f));
	for(int i=0;i<n;i++){
		dfs(i,-1,i);
	}
	ll ans=0;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			ans=max(ans,dp(i,j));
		}
	}
	printf("%lld\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值