【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

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;
}
posted @ 2017-04-02 08:46  CQzhangyu  阅读(378)  评论(0编辑  收藏  举报