牛客练习赛28 颓红警

题意:一颗有根树,每个节点有一权值,为敌人的战斗力,你的战斗力为p,每次可以攻击一节点,节点战斗力减p,他的儿子的战斗力减少p-dis^2,dis为他的儿子到他的距离。要将所有节点的战斗力变为小于0,求最小攻击次数。

题解:

题目描述有问题,看样例解释才明白有一条限制——只能攻击战斗力大于等于0的节点,由于题目没讲清楚,就容易自闭,想到dp什么错误的想法。有了这个限制,发现从根节点模拟就好了,问题在于维护当前结点已经受到的攻击力,才能算出需要攻击当前结点几次。

设 k=\sqrt p,那么影响一个结点的只有他的k个父亲了,设 第i个父亲被攻击了a_i次,tot_i当前结点受到的来自父亲们的攻击力,则(w_i-tot_i)/p就是攻击当前结点的次数。

tot_j=(\sum _{i=0}^ka_j)*p-\sum _{i=0}^ka_i*i^2

d表示当前结点的k级父亲被攻击的次数,tot_j给他的儿子的影响tot_{j+1}

tot_{j+1}=(\sum_{i=0}^{k-1}a_i )*p-\sum_{i=0}^{k-1}a_i*(i+1)^2

tot_{j+1}=(\sum_{i=0}^{k}a_i -d)*p-(\sum_{i=0}^{k-1}a_i*i^2+2\sum_{i=0}^{k-1}a_i*i+\sum_{i=0}^{k-1}a_i)

tot_{j+1}=(\sum_{i=0}^{k}a_i)*p-\sum_{i=0}^{k-1}a_i*i^2-2\sum_{i=0}^{k-1}a_i*i-\sum_{i=0}^{k-1}a_i-d*p

tot_{j+1}=(\sum_{i=0}^{k}a_i)*p-\sum_{i=0}^{k}a_i*i^2+d*k^2-2\sum_{i=0}^{k-1}a_i*i-\sum_{i=0}^{k-1}a_i -d*p

tot_{j+1}=tot_j+d*k^2-2\sum_{i=0}^{k}a_i*i+2*d*k-\sum_{i=0}^{k}a_i+d -d*p

tot的转移就是以上,接下来维护的是d\sum_{i=0}^{k}a_i*i\sum_{i=0}^{k}a_i三个变量

d的话由于是距离自己k级的父亲的关键次数,用一个数组存一路走到当前结点的所有经过结点的攻击次数,设当前为第dep层,那么数组中第dep-k个元素就是当前结点k级父亲的攻击次数

剩余的两个变量相信看过tot的转移后很好推出来了

代码:

#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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值