[練習][bzoj2783]二分+倍增 樹

題目背景
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;
}

本題結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章