Codeforces 1293 E. Xenon‘s Attack on the Gangs —— 树上记忆化搜索,单点加改成区间加,有丶东西

This way

题意:

现在有一棵大小为n的树,你要往边上放0~n-2这n-1个数,定义mex(u,v)表示u到v路径上的第一个未出现的自然数,定义S
在这里插入图片描述
问你S最大是多少。

题解:

我感觉这道题绝不止23的难度…其实这种将单点加值转换为区间加值的题目在以前的DP中也遇到过,在线段树的题目中也是经常遇到,但是这次却没有想到真实不应该。
首先需要知道的一件事情就是从0开始的值应当和之前的值的链接在一起:
Codeforces 1293 E. Xenon‘s Attack on the Gangs —— 树上记忆化搜索,单点加改成区间加,有丶东西_第1张图片
比如说0和1是这样放的,那么2就有两种放法,和他们粘在一起,这样构造出来的mex会尽可能的多。
那么求答案的时候就可以往下递归求,每次可以选择放在左端或者右端,然后由于只有这条包含0~链长-1的链会产生贡献,所以我们每次只需要找两个端点,然后进行记忆化搜索就行了。
那么我们怎么计算答案
∑ 1 ≤ u < v ≤ n m e x ( u , v ) \sum\limits_{1\leq u1u<vnmex(u,v)
→ ∑ m = 1 ∑ 1 ≤ u < v ≤ n m ( m e x ( u , v ) = m ) \rightarrow \sum\limits_{m=1}\sum\limits_{1\leq um=11u<vnm(mex(u,v)=m)
后面这个累加符号里的值的贡献是m,但是我们可以将一次加m转成m次加1,也就是值在<=m的时候都加上1,于是m的贡献最终还是m
→ ∑ m = 1 ∑ 1 ≤ u < v ≤ n 1 ( m e x ( u , v ) > = m ) \rightarrow \sum\limits_{m=1}\sum\limits_{1\leq u=m) m=11u<vn1(mex(u,v)>=m)
那么我们暴力地做树形DP
dp[i][j]表示点i到点j这条链的贡献
fa[i][j]表示以i为根的时候,j的父节点
siz[i][j]表示以i为根的时候,j的子树大小。
那么状态转移方程就是
dp[l][r]=siz[r][l]*siz[l][r]+max(dfs(fa[r][l],r),dfs(l,fa[l][r]));

#include
using namespace std;
#define ll long long
const int N=3e3+5;
ll dp[N][N],fa[N][N],siz[N][N];
vector<int>son[N];
void finds(int rt,int x){
    siz[rt][x]=1;
    for(int ne:son[x]){
        if(ne==fa[rt][x])continue;
        fa[rt][ne]=x;
        finds(rt,ne);
        siz[rt][x]+=siz[rt][ne];
    }
}
ll dfs(int l,int r){
    if(l==r)return 0;
    if(~dp[l][r])return dp[l][r];
    return dp[l][r]=siz[r][l]*siz[l][r]+max(dfs(fa[r][l],r),dfs(l,fa[l][r]));
}
int main()
{
    memset(dp,-1,sizeof(dp));
    int n,x,y;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
        scanf("%d%d",&x,&y),son[x].push_back(y),son[y].push_back(x);
    for(int i=1;i<=n;i++)
        finds(i,i);
    ll ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            ans=max(ans,dfs(i,j));
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(想法,dp,dfs)