Codeforces Round #614 (Div. 2) E. Xenon's Attack on the Gangs(DP記憶化搜索+思維)

題目鏈接:https://codeforces.com/contest/1293/problem/E

 

題目大意:

  給出一棵樹,要求給樹的每條邊賦權值,使得樹中所有點的mex(x,y)mex(x,y)和最大,並輸出這個和
  mex(x,y)mex(x,y)指的是從x到y的路徑中,沒出現過的整數的最小值

 

題目思路:

  推薦qsc的題解視頻:傳送門
  很容易發現,在這個條件下,一定要有人做出犧牲,也就是隻有一條鏈上能夠從小到大接連着,還可以發現的是,只要有一條邊選擇了0,那麼這條邊兩頭的點滿足mex(x,y)1mex(x,y)\ge1,因爲他們中最小的整數爲0,也就是對於這樣的一條連着的整數鏈來說,比如0,1,2,這樣的鏈兩邊的點形成的鏈值至少爲3,這也就是這道題的精髓。
  可以發現,這題n只有3000,所以支持n^2,那麼就很美滋滋,剛纔發現,從小到大接着的鏈非常重要,所以直接n2n^2枚舉這條鏈。首先需要一個預處理,fa[root][u]fa[root][u]表示以root爲根,u的父親是誰,num[root][u]num[root][u]表示以root爲根,以u爲頂點的子樹有多少點。然後使用dp[x][y]dp[x][y]來表示x,y爲鏈的情況下的最大值是多少。如何dp呢?假設求1-2-3-4這樣的鏈的結果,那麼假如我們已經知道了它的子結果,1-2-3和2-3-4的結果,我們要從這個結果推出1-2-3-4結果,那麼根據我們的分析,從1-2-3和2-3-4變成1-2-3-4都是加上1-2-3-4這條鏈兩頭點的乘積(就是這棵樹中有多少條鏈能夠覆蓋這條鏈,他們都能得到這個加成),既然加的數值一樣,那麼肯定選他們兩個中大的那個啦。數量的話很簡單,對於x-…-y這條鏈來說,x那邊的點的個數就是以y爲根,x爲子樹頂點的子樹的點數量,另一邊就正好相反,就是剛纔說的num[root][u]num[root][u]可以得到,而1-2-3和2-3-4的獲得,就可以用fa[root][u]fa[root][u],同樣對於x-…-y這條鏈來說,fa[x][y]fa[x][y]就是x-…-y這條鏈中,與y直接相連的那個點。
  感覺這題非常巧妙,有很多可以學習的點,是一個好題。感覺講的已經比較清楚,代碼也比較剪短可以結合代碼學習。如果還有有問題的地方歡迎評論區提出一起討論~

 

以下是代碼:

#include<bits/stdc++.h>

using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define inf 0x3f3f3f3f
const int MAXN = 3e3+5;
vector<int>v[MAXN];
ll fa[MAXN][MAXN],num[MAXN][MAXN],dp[MAXN][MAXN],root;
void dfs(int u,int f){
    num[root][u]=1;
    int len=v[u].size();
    rep(i,0,len-1){
        int y=v[u][i];
        if(y==f)continue;
        fa[root][y]=u;
        dfs(y,u);
        num[root][u]+=num[root][y];
    }
}
ll solve(int x,int y){
    if(x==y)return 0;
    if(dp[x][y])return dp[x][y];
    return dp[x][y]=num[x][y]*num[y][x]+max(solve(x,fa[x][y]),solve(y,fa[y][x]));
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,x,y;
    while(cin>>n){
        rep(i,1,n)v[i].clear();
        memset(fa,0,sizeof(fa));
        memset(num,0,sizeof(num));
        memset(dp,0,sizeof(dp));
        rep(i,1,n-1){
            cin>>x>>y;
            v[x].push_back(y);
            v[y].push_back(x);
        }
        rep(i,1,n){
            root=i;
            dfs(i,-1);
        }
        ll ans=0;
        rep(i,1,n){
            rep(j,i+1,n){
                ans=max(ans,solve(i,j));
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章