特殊数据p=1,当然就把所有点权求和就是答案
因为是要最小化进攻次数
考虑到打下面的上面死不了,不妨先把上面的打死
贪心正确性显然
然后从上到下,算出当前这个打几下死,把伤害传递下去,这样就有70%的分了(数据造水了)
1000000的数据,考虑线性解法
看这个式子Max(0,p-dis(i,j)^2)
因为j在i的子树内
所以等价于Max(0,p-(deep[i]-deep[j])^2)
考虑一个点j受到的来自祖先的伤害
Hurt=∑(p-(deep[i]-deep[j])^2)((deep[i]-deep[j])^2<p)
=∑(p-deep[j]^2-deep[i]^2+2*deep[i]*deep[j])((deep[i]-deep[j])^2<p)
其中,p,deep[j]是已知量,我们在递归时只要记录有效伤害个数,有效伤害深度和,有效伤害深度平方和即可
理解了解法,具体实现还是很简单的,这里就不在赘述
难看的代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#define int long long
#define ll long long
using namespace std;
const int maxn=1e6+10;
struct edge{
int next,to;
}e[maxn*2];
int n,num[maxn],head[maxn],cnt,line[maxn],d[maxn],dis,cnts[maxn],m[maxn];
ll sum[maxn],sump[maxn],mid;
inline void add(int x,int y)
{
e[++cnt].next=head[x];
e[cnt].to=y;
head[x]=cnt;
}
void dfs(int x,int pre,int deep)
{
d[x]=deep;
if(d[x]>dis)
{
int tmp=d[x]-dis;
line[x]-=cnts[tmp];
sum[x]-=(ll)cnts[tmp]*tmp;
sump[x]-=(ll)cnts[tmp]*tmp*tmp;
}
ll all=line[x]*(mid-(ll)d[x]*d[x])-sump[x]+2*d[x]*sum[x];
if(m[x]>=all)
{
num[x]=(m[x]-all)/mid+1;
cnt+=num[x];
}
cnts[d[x]]=num[x];
for(int v,i=head[x];i;i=e[i].next)
if((v=e[i].to)!=pre)
{
line[v]=line[x]+num[x];
sum[v]=sum[x]+(ll)num[x]*d[x];
sump[v]=sump[x]+(ll)num[x]*d[x]*d[x];
dfs(v,x,deep+1);
}
}
inline void check()
{
cnt=0,dis=ceil(sqrt(mid));
dfs(1,0,1);
}
int readn()
{
int x=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
{
x=(x<<3)+(x<<1)+(c^'0');
c=getchar();
}
return x;
}
signed main()
{
n=readn(),mid=readn();
for(int i=1;i<=n;i++)
m[i]=readn();
for(int x,y,i=1;i<n;i++)
{
x=readn(),y=readn();
add(x,y),add(y,x);
}
check();
printf("%lld
",cnt);
return 0;
}