题意:一颗有根树,每个节点有一权值,为敌人的战斗力,你的战斗力为p,每次可以攻击一节点,节点战斗力减p,他的儿子的战斗力减少p-dis^2,dis为他的儿子到他的距离。要将所有节点的战斗力变为小于0,求最小攻击次数。
题解:
题目描述有问题,看样例解释才明白有一条限制——只能攻击战斗力大于等于0的节点,由于题目没讲清楚,就容易自闭,想到dp什么错误的想法。有了这个限制,发现从根节点模拟就好了,问题在于维护当前结点已经受到的攻击力,才能算出需要攻击当前结点几次。
设 ,那么影响一个结点的只有他的k个父亲了,设 第个父亲被攻击了次,当前结点受到的来自父亲们的攻击力,则就是攻击当前结点的次数。
设表示当前结点的k级父亲被攻击的次数,给他的儿子的影响为
的转移就是以上,接下来维护的是,,三个变量
的话由于是距离自己k级的父亲的关键次数,用一个数组存一路走到当前结点的所有经过结点的攻击次数,设当前为第层,那么数组中第个元素就是当前结点k级父亲的攻击次数
剩余的两个变量相信看过的转移后很好推出来了
代码:
#include<bits/stdc++.h>
#define N 1000010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define mod 998244353
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define ub upper_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scl(x) scanf("%lld",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
int a[N],b[N],c[N],cnt;
LL w[N],sumii[N],sumi[N],q[N],tot[N],k,ans,n,p;
inline void add(int x,int y)
{
a[++cnt]=y; c[cnt]=b[x]; b[x]=cnt;
}
void dfs(int x,int dep)
{
LL d=dep-1>k?q[dep-1-k]:0;
tot[dep]=tot[dep-1]-d*p+d*k*k-sumii[dep-1]*2+d*k*2-sumi[dep-1]+d;
LL delta=max(0ll,w[x]-tot[dep]+1);
if (delta) q[dep]=(delta-1)/p+1;else q[dep]=0;
ans+=q[dep];
tot[dep]+=q[dep]*p;
sumii[dep]=sumii[dep-1]-d*k+sumi[dep-1]-d;
sumi[dep]=sumi[dep-1]-d+q[dep];
for (int i=b[x];i;i=c[i]) dfs(a[i],dep+1);
}
int main()
{
scl(n); scl(p); k=sqrt(p);
for (int i=1;i<=n;i++) scl(w[i]);
for (int i=1;i<n;i++)
{
int x,y; scc(x,y);
add(x,y);
}
dfs(1,1);
printf("%lld\n",ans);
return 0;
}