題目背景
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(
代碼
#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;
}
本題結。