题目描述
在这个问题中,给定一个值S和一棵树。在树的每个节点有一个正整数,问有多少条路径的节点总和达到S。路径中节点的深度必须是升序的。假设节点1是根节点,根的深度是0,它的儿子节点的深度为1。路径不必一定从根节点开始。
输入输出格式
输入格式:
第一行是两个整数N和S,其中N是树的节点数。 第二行是N个正整数,第i个整数表示节点i的正整数。 接下来的N-1行每行是2个整数x和y,表示y是x的儿子。
输出格式:
输出路径节点总和为S的路径数量。
输入输出样例
输入样例#1:
3 3 1 2 3 1 2 1 3
输出样例#1:
2
说明
对于100%数据,N<=100000,所有权值以及S都不超过1000。
题目大意:求树上连续一段深度递增的路径的点权和为s的条数
题解:dfs(i)以i为起点的路径有多少条
错因:理解错了 不能用记忆化搜索
数据水暴力可过
代码:
#include<iostream> #include<cstdio> #include<cstring> #define LL long long #define maxn 100008 using namespace std; int n,s,sumedge; int head[maxn],w[maxn]; long long ans; struct Edge{ int x,y,nxt; Edge(int x=0,int y=0,int nxt=0): x(x),y(y),nxt(nxt){} }edge[maxn]; void add(int x,int y){ edge[++sumedge]=Edge(x,y,head[x]); head[x]=sumedge; } LL dfs(int x,int sum){ if(sum>s)return 0; if(sum==s)return 1; long long js=0; for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; js+=dfs(v,sum+w[v]); } return js; } int main(){ scanf("%d%d",&n,&s); for(int i=1;i<=n;i++)scanf("%d",&w[i]); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); } for(int i=1;i<=n;i++)if(w[i]==s)ans++;else ans+=dfs(i,w[i]); cout<<ans<<endl; return 0; }
树上前缀和
保存搜到i之前的祖先,累加权值,是否sum[i]-sum[祖先]=s,注意搜完时删掉祖先。
#include<iostream> #include<cstdio> #include<cstring> #define maxn 100008 #define LL long long using namespace std; int n,s,sumedge,cnt,js; int head[maxn],w[maxn],dad[maxn],fa[maxn],sum[maxn]; LL ans; struct Edge{ int x,y,nxt; Edge(int x=0,int y=0,int nxt=0): x(x),y(y),nxt(nxt){} }edge[maxn]; void add(int x,int y){ edge[++sumedge]=Edge(x,y,head[x]); head[x]=sumedge; } void dfs(int x){ dad[++js]=x; for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; sum[v]=sum[x]+w[v]; for(int j=js;j>=0;j--){//要循环到0,可能它自己的点权就是s if(sum[v]-sum[dad[j]]==s)ans++; if(sum[v]-sum[dad[j]]>s)break; } dfs(v); } js--; } int main(){ scanf("%d%d",&n,&s); for(int i=1;i<=n;i++)scanf("%d",&w[i]); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); fa[y]=x; add(x,y); } sum[1]=w[1]; dfs(1); cout<<ans<<endl; return 0; }
时间: 2024-10-08 19:14:29