CodeForces - 1292C Xenon‘s Attack on the Gangs(思维+dp)

题目链接:点击这里

题目大意:
给出一颗有 n n n 个节点的数,将 [ 0 , n − 2 ] [0,n-2] [0,n2] 内的每个整数各用一次分配到这颗树的 n − 1 n-1 n1 条边上,求 ∑ 1 ≤ i < j ≤ n m e x ( i , j ) \sum_{1\le i<j\le n}mex(i,j) 1i<jnmex(i,j) 的最大值,其中 m e x ( i , j ) mex(i,j) mex(i,j) 指的是从 i i i j j j 的路径上没有出现的最小非负数。

题目分析:
我们考虑从样例二分析,样例二的构造方案如图所示:
在这里插入图片描述
我们先考虑 0 0 0 的情况贡献:由于左边有 1 , 2 , 4 1,2,4 1,2,4 3 3 3 个点,右边有 3 , 5 3,5 3,5 两个点,故贡献为 0 ∗ 3 ∗ 2 0*3*2 032
m e x = v a l mex=val mex=val ,链左边有 l x lx lx 个点,右边有 r x rx rx 个点,不难发现,我们贡献就是 v a l ∗ l x ∗ r x val*lx*rx vallxrx
我们从易到难的想,先思如果树为一条链时如何处理:
定义状态 d p [ i ] [ j ] ( i < j ) dp[i][j](i<j) dp[i][j](i<j) 表示以 i , j i,j i,j 为端点链用从 0 0 0 开始的连续自然数赋值时的题目所求答案是多少,设 l c n t [ i ] lcnt[i] lcnt[i] 表示 i i i 左边有多少个点, r c n t [ i ] rcnt[i] rcnt[i] 表示 i i i 右边有多少个点
故有转移方程:
d p [ i ] [ j ] = l c n t [ i ] ∗ r c n t [ j ] + m a x ( d p [ i + 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j]=lcnt[i]*rcnt[j]+max(dp[i+1][j],dp[i][j-1]) dp[i][j]=lcnt[i]rcnt[j]+max(dp[i+1][j],dp[i][j1])
而由于 n n n 只有 3000 3000 3000 ,所以我们就可以 O ( n 2 ) O(n^2) O(n2) 枚举端点,然后处理链的贡献,为了避免状态被重复计算,所以采用记忆化的方式进行 d p dp dp ,这样就保证了 d p dp dp 的复杂度为 O ( n 2 ) O(n^2) O(n2)
当在树上处理链时,我们需要预处理 f a [ x ] [ y ] , c n t [ x ] [ y ] fa[x][y],cnt[x][y] fa[x][y],cnt[x][y] ,其分别表示以 x x x 为根时 y y y 的父节点是谁,以 x x x 为根时 以 y y y 为根的子树大小,这样状态转移方程就可以写成:
d p [ x ] [ y ] = c n t [ y ] [ x ] ∗ c n t [ x ] [ y ] + m a x ( d p [   f a [ y ] [ x ]   ] [ y ] , d p [   f a [ x ] [ y ]   ] [ x ] ) dp[x][y] = cnt[y][x]*cnt[x][y]+max(dp[\ fa[y][x]\ ][y],dp[\ fa[x][y]\ ][x]) dp[x][y]=cnt[y][x]cnt[x][y]+max(dp[ fa[y][x] ][y],dp[ fa[x][y] ][x])

具体细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<queue>
#define ll long long
#define inf 0x3f3f3f3f
#define Inf 0x3f3f3f3f3f3f3f3f
#define int ll
using namespace std;
int read()
{
	int res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 3e3+5;
const int mod = 1e9+7;
const double pi = acos(-1);
const double eps = 1e-8;
int n,fa[maxn][maxn],dp[maxn][maxn],cnt[maxn][maxn];
vector<int>g[maxn];
void dfs(int u,int f,int root)
{
	cnt[root][u] = 1;
	fa[root][u] = f;
	for(auto to:g[u])
	{
		if(to == f) continue;
		dfs(to,u,root);
		cnt[root][u] += cnt[root][to];
	}
}
int solve(int x,int y)
{
	if(x == y) return 0;
	if(dp[x][y] != -1) return dp[x][y];
	dp[x][y] = cnt[y][x]*cnt[x][y]+max(solve(fa[y][x],y),solve(fa[x][y],x));
	return dp[x][y];
}
signed main()
{
	n = read();
	for(int i = 1;i < n;i++)
	{
		int from = read(),to = read();
		g[from].push_back(to);
		g[to].push_back(from);
	}
	for(int i = 1;i <= n;i++) dfs(i,0,i);
	int ans = 0;
	memset(dp,-1,sizeof(dp));
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= n;j++) ans = max(ans,solve(i,j));
	cout<<ans<<endl;
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
引用\[1\]中提到了一种树形动态规划的方法来解决CodeForces - 982C问题。在这个问题中,subtree指的是子连通块,而不是子树。为了使cnt_white - cnt_black尽可能大,可以使用两次树形动态规划来求解。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。如果ans\[u\]为正,则减去dp\[v\]就是树链所代表的子树的权值。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] 问题: CodeForces - 982C 树形DP是什么问题?如何解决? 回答: CodeForces - 982C是一个树形动态规划问题。在这个问题中,需要求解子连通块的最大权值和,使得cnt_white - cnt_black尽可能大。解决这个问题的方法是使用两次树形动态规划。第一次是自底向上的过程,维护一个dp数组,表示以每个节点为根的子树中的最大连通块。第二次是自顶向下的过程,处理自底向上过程中无法包含的树链所代表的子树。在第二次遍历中,需要维护一个sum变量,用于存储树链所代表的子树的贡献。根据ans\[u\]的正负,决定是否能对相邻的子节点做出贡献。最终,ans\[u\]代表包含节点u在内的子连通块的最大权值。\[1\] #### 引用[.reference_title] - *1* *2* [CodeForces - 1324F Maximum White Subtree(树形dp)](https://blog.csdn.net/qq_45458915/article/details/104831678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值