某不知名的樹形Dp

題意

給出一個\(N\)個點的樹,找出一個點來,以這個點爲根的樹時,全部點的深度之和最大spa

分析

看到樹,還讓求最大,這種可能不是貪心就是樹形\(DP\),貪心的話樹的形狀無法判斷,果斷放棄,那麼就只能是\(DP\)了。
既然它讓求深度之和,那麼我就直接定義以\(i\)爲根時深度和爲\(DP_i\),接下來就是怎麼轉移的問題了。若是我枚舉每一個點來考慮,那麼還要計算它下邊的子樹和它上邊的子樹,顯然是很差弄,時間複雜度可能在\(O(N^2)\)左右,雖然時間十秒但也不夠用啊,因爲\(n\)大到了1000000,因此這個題仍是得用\(O(n)\)的效率,若是我以某種手段獲得了\(DP_1\),那麼接下來的轉移就好說了,每次往下找一個兒子\(v\),深度減少了\(siz_v\),增長了\(n-siz_v\),這樣就能用兩個\(O(n)\)來完成這個題,最後在\(O(n)\)的統計一下答案就好。code

#include<cstdio>
using namespace std;
const int N=1e6+10;
struct Edge{
    int to,nxt;
}e[N<<1];
int dep[N],Head[N],len;
void Ins(int a,int b){
    e[++len].to=b;e[len].nxt=Head[a];Head[a]=len;
}
int dp[N],siz[N];
void dfs(int u,int fa){
    siz[u]=1;dp[u]=dep[u];
    for(int i=Head[u];i;i=e[i].nxt){
        int v=e[i].nxt;
        if(v==fa)continue;
        dep[v]=dep[u]+1;
        dfs(v,u);
        siz[u]+=siz[v];
        dp[u]+=dp[v];
    }
}    
int n;
void calc(int u,int fa){
    for(int i=Head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa)continue;
        dp[v]=dp[u]-siz[v]+n-siz[v];
        calc(v,u);
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        Ins(a,b);Ins(b,a);
    }
    dfs(1,0);
    calc(1,0);
    int ans=0;
    for(int i=1;i<=n;i++)
        if(dp[ans]<dp[i])ans=i;
    printf("%d\n",ans);
}

總結

轉移方程題目問什麼設什麼先,求不出來再考慮換或者輔助一下\(DP\).io

相關文章
相關標籤/搜索