【CF580C】Kefa and Park
阿新 • • 發佈:2018-12-01
題目大意:給定一棵 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; }