【BZOJ2783】[JLOI2012]树 DFS+栈+队列
【BZOJ2783】[JLOI2012]树
Description
在这个问题中,给定一个值S和一棵树。在树的每个节点有一个正整数,问有多少条路径的节点总和达到S。路径中节点的深度必须是升序的。假设节点1是根节点,根的深度是0,它的儿子节点的深度为1。路径不必一定从根节点开始。
Input
第一行是两个整数N和S,其中N是树的节点数。
第二行是N个正整数,第i个整数表示节点i的正整数。
接下来的N-1行每行是2个整数x和y,表示y是x的儿子。
Output
输出路径节点总和为S的路径数量。
Sample Input
3 3
1 2 3
1 2
1 3
1 2 3
1 2
1 3
Sample Output
2
HINT
对于100%数据,N≤100000,所有权值以及S都不超过1000。
题解:本题可以用各种O(nlogn)的算法水过,但是O(n)当然也可以搞~
UPD:我写的这什么玩意,这个东西不是O(n)的,只不过因为S不超过1000所以可以过。
用我们在DFS的时候,始终维护这样一个队列,使它是一条连续的链,且队列中点的权值和不超过S,具体做法如下
在DFS进栈的时候,我们将这个点压入队尾,并不断弹出队首直到队列的权值和≤S,并更新答案
在DFS弹栈的时候,我们将这个点从队尾弹出,并将队列恢复成这个点进栈之前的样子(由于后进来的元素都在这个点之后,所以不会修改队列中这个点前面的点,所以只需要记录一下当时的队首位置就行了)
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxn=100010; int n,m,cnt,h,t,top,ans; int fa[maxn],to[maxn],next[maxn],v[maxn],head[maxn],q[maxn],dep[maxn]; void add(int a,int b) { to[cnt]=b; next[cnt]=head[a]; head[a]=cnt++; } void dfs(int x) { int temp=h,i; q[++t]=x; while(dep[x]-dep[q[h]]>m) h++; if(dep[x]-dep[q[h]]==m) ans++; for(i=head[x];i!=-1;i=next[i]) { dep[to[i]]=dep[x]+v[to[i]]; dfs(to[i]); } h=temp,t--; } int main() { scanf("%d%d",&n,&m); int i,a,b,c; for(i=1;i<=n;i++) scanf("%d",&v[i]); memset(head,-1,sizeof(head)); for(i=1;i<n;i++) { scanf("%d%d",&a,&b); add(a,b); } dep[1]=v[1],h=0,t=0,dfs(1); printf("%d",ans); return 0; }
| 欢迎来原网站坐坐! >原文链接<