题意:
给你一个树,根节点为1,然后每个点可以做蛋糕,但是只能送到从根节点到这个点的最简路径上的点,也就是1到当前点这条链上的点。但是每个点做的蛋糕有个保质期,如果送的长度>保质期就不行。现在问你每个点可以有多少点送蛋糕给自己。
思考:
1.很明显就是一个点的所有儿子到这个点的距离<=儿子的权值。由于是对每个点都输出答案,我就总感觉是树上启发式,但是好久没用了,有些淡忘,但是我看了看以前做过的启发式的题,感觉这题不能用启发式啊,因为每个点他所作的贡献并不是全局的,他做的贡献还要和祖宗节点比一下大小,如果保质期够大才行,但是启发式合并不行,因为你要保证每个点的贡献在不同的子树上是一样的才行。所以启发式就不行了,不过也要抽空好好复习一下。
2.但是这题过的也很快,我就感觉肯定不是启发式。我画了画图,对于每个点,他可以每次让这个点到上面某个点这条链上所有的点的答案都加1,因为他都可以送到。想到了区间加,我就想到了树上差分,后缀差分后求个后缀和就可以了。对于每个点他能送到的最祖先的点是谁的话,这里就可以像lca一样维护一个倍增,对于距离a点<=b的最远的点,可以每次倍增跳就行了。然后差分的时候注意!sum[acc[up][0]]-1,sum[down]+1。不是sum[up-1]+1。当时写的时候写顺手了,直接当数组用了。多多注意细节,仔细检查再提交。
代码:
#include<bits/stdc++.h>
#define int long long
#define pb push_back
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
using namespace std;
const int N = 2e6+10;
int T,n,m;
int va[N];
int acc[N][25],dep[N],cnt=22;
int sum[N];
vector<int > e[N];
void dfs(int now,int p)
{
acc[now][0] = p;
dep[now] = dep[p]+1;
for(auto spot:e[now])
{
if(spot==p) continue;
dfs(spot,now);
}
}
int query(int a,int b)
{
int now = a,sum = b;
for(int i=cnt;i>=0;i--)
{
if(sum>=(1ll<<i))
{
sum -= (1ll<<i);
now = acc[now][i];
}
}
return now;
}
void dfst(int now,int p)
{
int up = query(now,va[now]);
int down = now;
up = acc[up][0];
sum[up]--;
sum[down]++;
for(auto spot:e[now])
{
if(spot==p) continue;
dfst(spot,now);
}
}
void get(int now,int p)
{
for(auto spot:e[now])
{
if(spot==p) continue;
get(spot,now);
sum[now] += sum[spot];
}
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%lld%lld",&a,&b);
e[a].pb(b);
e[b].pb(a);
}
for(int i=1;i<=n;i++) cin>>va[i];
dfs(1,0);
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=n;j++)
acc[j][i] = acc[acc[j][i-1]][i-1];
}
dfst(1,0);get(1,0);
for(int i=1;i<=n;i++) printf("%lld ",sum[i]);
return 0;
}
总结:
多多思考,仔细检查,认真仔细,别慌,不用急。