bzoj2783【JLOI2012】树

2783: [JLOI2012]树

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 668  Solved: 389
[ Submit][ Status][ Discuss]

Description

数列
提交文件:sequence.pas/c/cpp
输入文件:sequence.in
输出文件:sequence.out
问题描述:
把一个正整数分成一列连续的正整数之和。这个数列必须包含至少两个正整数。你需要求出这个数列的最小长度。如果这个数列不存在则输出-1。
输入格式:
每行包含一个正整数n。
每个文件包含多行,读入直到文件结束。
输出格式:
对于每个n,输出一行,为这个数列的最小长度。
 

       第一行是两个整数N和S,其中N是树的节点数。

       第二行是N个正整数,第i个整数表示节点i的正整数。

       接下来的N-1行每行是2个整数x和y,表示y是x的儿子。

输出格式:

       输出路径节点总和为S的路径数量。

 

输入样例:

输出样例:

3 3

1 2 3

1 2

1 3

2

 

数据范围:

对于30%数据,N≤100;

对于60%数据,N≤1000;

对于100%数据,N≤100000,所有权值以及S都不超过1000。

数据范围:
对于所有数据,n≤2 63

这个是JLOI2012的T1,发出来仅为了试题完整

=============================================================================================

       在这个问题中,给定一个值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。

Source




用f[i][j]表示i的第2^j个祖先,可以用倍增法预处理。

对于每一个点,二分第k个祖先,比较两点距离和s的大小。

总的时间复杂度O(nlog^2n)。




#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 100005
#define inf 1000000000
using namespace std;
struct edge_type
{
	int next,to;
}e[maxn];
int a[maxn],d[maxn],dep[maxn],in[maxn],head[maxn],f[maxn][20];
int n,s,x,y,root,ans=0,cnt=0;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void add_edge(int x,int y)
{
	e[++cnt]=(edge_type){head[x],y};
	head[x]=cnt;
	in[y]++;f[y][0]=x;
}
inline void dfs(int x)
{
	for(int i=head[x];i;i=e[i].next)
	{
		int y=e[i].to;
		d[y]=d[x]+a[y];dep[y]=dep[x]+1;
		dfs(y);
	}
}
inline int find(int x,int k)
{
	int t=0,p=x;
	while (k)
	{
		if (k&1) p=f[p][t];
		t++;k>>=1;
	}
	return p;
}
inline bool check(int x)
{
	int l=0,r=dep[x],mid,tmp;
	while (l<=r)
	{
		mid=(l+r)>>1;
		tmp=find(x,mid);
		if (d[x]-d[tmp]==s) return true;
		else if (d[x]-d[tmp]<s) l=mid+1;
		else r=mid-1;
	}
	return false;
}
int main()
{
	n=read();s=read();
	F(i,1,n) a[i]=read();
	F(i,1,n-1)
	{
		x=read();y=read();
		add_edge(x,y);
	}
	F(i,1,n) if (!in[i]){root=i;break;}
	d[root]=a[root];dep[root]=1;f[root][0]=0;
	dfs(root);
	for(int j=1;(1<<j)<=n;j++) F(i,1,n) f[i][j]=f[f[i][j-1]][j-1];
	F(i,1,n) if (check(i)) ans++;
	printf("%d\n",ans);
}


你可能感兴趣的:(二分,bzoj,倍增)