1. 程式人生 > >【CF580C】Kefa and Park

【CF580C】Kefa and Park

題目大意:給定一棵 N 個節點的有根樹(其中根節點始終為 1 號節點),點有點權,點權只有 1 和 0 兩種,求從根節點到葉子節點的路徑中,有多少條路徑滿足:路徑上最大連續點權為 1 的節點個數不超過 M。

題解:一開始想到了樹形dp,即:\(dp[i][0/1]\) 表示從根節點到該點的路徑中不選/選該點的最大連續 1 的個數,則狀態的轉移涉及到該節點的 \(val\) 值和其父節點的 \(val\) 值。不過,其實沒有必要記錄下每一個節點的狀態,原因是:如果到當前節點的連續 1 的個數已經超過了限制,那麼後面無論怎樣,這條路徑都對答案沒有貢獻。因此,可以直接記錄下到當前節點連續的長度,超過限制直接剪枝,由剪枝操作還可以知道,若當前節點的權值是 0,無論前面的值是多少,都對後面的值沒有任何影響。

另外:建樹的時候應該新增雙向邊,因為題目資料中給的關係並不是嚴格的父子關係。

樹形 dp 程式碼如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;

inline int read(){
    int x=0,f=1;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
    do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
    return f*x;
}

struct node{
    int nxt,to;
}e[maxn<<1];
int tot=1,head[maxn];
inline void add_edge(int from,int to){
    e[++tot]=node{head[from],to},head[from]=tot;
}
int n,m,ans,val[maxn],dp[maxn][2];

void read_and_parse(){
    n=read(),m=read();
    for(int i=1;i<=n;i++)val[i]=read();
    for(int i=1;i<n;i++){
        int from=read(),to=read();
        add_edge(from,to),add_edge(to,from);
    }
}

void dfs(int u,int fa){
    if(!val[u])dp[u][0]=max(dp[fa][0],dp[fa][1]);
    else if(!val[fa])dp[u][1]=1,dp[u][0]=dp[fa][0];
    else dp[u][1]=dp[fa][1]+1,dp[u][0]=max(dp[fa][1],dp[fa][0]);
    int ok=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;if(v==fa)continue;
        ok=0,dfs(v,u);
    }
    if(ok&&max(dp[u][0],dp[u][1])<=m)++ans;
}

void solve(){
    dfs(1,0);
    printf("%d\n",ans);
}

int main(){
    read_and_parse();
    solve();
    return 0;
}

優化後代碼如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;

inline int read(){
    int x=0,f=1;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
    do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
    return f*x;
}

struct node{
    int nxt,to;
}e[maxn<<1];
int tot=1,head[maxn];
inline void add_edge(int from,int to){
    e[++tot]=node{head[from],to},head[from]=tot;
}
int n,m,ans,val[maxn];

void read_and_parse(){
    n=read(),m=read();
    for(int i=1;i<=n;i++)val[i]=read();
    for(int i=1;i<n;i++){
        int from=read(),to=read();
        add_edge(from,to),add_edge(to,from);
    }
}

void dfs(int u,int fa,int cnt){
    if(cnt>m)return;
    bool flag=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;if(v==fa)continue;
        flag=0;
        if(val[v])dfs(v,u,cnt+1);
        else dfs(v,u,0);
    }
    ans+=flag;
}

void solve(){
    dfs(1,0,val[1]);
    printf("%d\n",ans);
}

int main(){
    read_and_parse();
    solve();
    return 0;
}