[练习][bzoj2783]二分+倍增 树

来源:互联网 发布:js实现鼠标特效 编辑:程序博客网 时间:2024/06/05 16:45

题目背景
bzoj2783

题目描述
给定一个值S和一棵树。在树的每个节点有一个正整数,问有多少条路径的节点总和达到S。路径中节点的深度必须是升序的。假设节点1是根节点,根的深度是0,它的儿子节点的深度为1。路径不必一定从根节点开始。

输入格式
第一行是两个整数N和S,其中N是树的节点数。
第二行是N个正整数,第i个整数表示节点i的正整数。
接下来的N-1行每行是2个整数x和y,表示y是x的儿子。

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

样例数据
输入

3 3
1 2 3
1 2
1 3

输出

2

备注
【数据范围】
对于100%数据,N≤100000,所有权值以及S都不超过1000。

分析:题目中的“路径中节点的深度必须是升序的”已经提示了倍增的方法找father,我们能在O(logN)的时间内求出一个点到他的第n个爸爸之间所有点的点权之和,又点的权值是为正的,满足二分的单调性,所以就二分+倍增啦。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>#include<queue>#include<set>using namespace std;int getint(){    int sum=0,f=1;    char ch;    for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());    if(ch=='-')    {        f=-1;        ch=getchar();    }    for(;isdigit(ch);ch=getchar())        sum=(sum<<3)+(sum<<1)+ch-48;    return sum*f;}const int maxn=100010;int n,s,ans;int w[maxn];int tot,first[maxn],nxt[maxn*2],to[maxn*2];int fa[maxn][20],sumw[maxn][20];void addedge(int x,int y){    tot++;    nxt[tot]=first[x];    first[x]=tot;    to[tot]=y;    tot++;    nxt[tot]=first[y];    first[y]=tot;    to[tot]=x;}void dfs(int u,int f)//预处理fa、权值和{    fa[u][0]=f;    sumw[u][0]=w[f];    for(int mi=1;mi<=19;++mi)    {        fa[u][mi]=fa[fa[u][mi-1]][mi-1];        sumw[u][mi]=sumw[u][mi-1]+sumw[fa[u][mi-1]][mi-1];    }    for(int p=first[u];p;p=nxt[p])    {        int v=to[p];        if(v!=f)            dfs(v,u);    }}int getw(int x,int dep)//计算从fa[x][0]到dep的权值和{    int res=0;    for(int i=19;i>=0;--i)        if(dep&(1<<i))        {            res+=sumw[x][i];            x=fa[x][i];        }    return res;}int erfen(int x)//二分向上跳的深度{    int l=0,r=100000,mid;    while(l<=r)    {        mid=l+r>>1;        int sumww=getw(x,mid)+w[x];        if(sumww<s) l=mid+1;        else if(sumww>s) r=mid-1;        else return 1;    }    return 0;}int main(){    freopen("tree.in","r",stdin);    freopen("tree.out","w",stdout);    int x,y;    n=getint(),s=getint();    for(int i=1;i<=n;++i)        w[i]=getint();    for(int i=1;i<n;++i)    {        x=getint(),y=getint();        addedge(x,y);    }    dfs(1,0);    ans=0;    for(int i=1;i<=n;++i)        ans+=erfen(i);    cout<<ans<<'\n';    return 0;}

本题结。