NC19314 颓红警(DFS+树形DP)

题目链接

题意:
有 一 棵 树 代 表 敌 人 的 分 级 网 络 , n 个 结 点 有一棵树代表敌人的分级网络,n个结点 n
从 上 到 下 依 次 等 级 地 位 下 降 , 每 个 结 点 代 表 一 个 部 队 从上到下依次等级地位下降,每个结点代表一个部队
每 个 部 队 有 攻 击 力 a i , 每 次 可 以 使 一 个 结 点 的 攻 击 力 减 少 p 每个部队有攻击力a_i,每次可以使一个结点的攻击力减少p ai使p
同 时 这 个 结 点 的 子 节 点 会 减 少 p − d i s ( i , j ) 2 同时这个结点的子节点会减少p-dis(i,j)^2 pdis(i,j)2
如 果 攻 击 力 小 于 0 , 那 么 将 被 歼 灭 如果攻击力小于0,那么将被歼灭 0
只 能 对 未 被 歼 灭 的 部 队 发 动 进 攻 只能对未被歼灭的部队发动进攻
问 最 多 多 少 次 能 够 歼 灭 这 个 军 队 问最多多少次能够歼灭这个军队
题解:
n < = 1 e 6 , a i < = 1 e 9 n<=1e6,a_i<=1e9 n<=1e6,ai<=1e9
由 于 是 个 树 , 而 且 儿 子 和 父 亲 有 关 系 由于是个树,而且儿子和父亲有关系
最 先 考 虑 的 就 是 D F S 遍 历 然 后 进 行 状 态 转 移 最先考虑的就是DFS遍历然后进行状态转移 DFS
如 果 攻 击 父 结 点 , 就 会 对 子 结 点 造 成 伤 害 如果攻击父结点,就会对子结点造成伤害
这 个 伤 害 值 为 p − d i s ( i , j ) 2 这个伤害值为p-dis(i,j)^2 pdis(i,j)2
对 这 个 进 行 化 简 , 可 以 得 到 p − ( d j − d i ) 2 对这个进行化简,可以得到p-(d_j-d_i)^2 p(djdi)2
( d 表 示 结 点 的 深 度 ) (d表示结点的深度) (d)
所 以 现 在 需 要 看 的 就 是 每 个 父 亲 对 该 结 点 的 影 响 所以现在需要看的就是每个父亲对该结点的影响
通 过 这 个 式 子 我 们 可 以 发 现 超 过 父 结 点 只 能 对 一 定 距 离 的 子 节 点 造 成 伤 害 通过这个式子我们可以发现超过父结点只能对一定距离的子节点造成伤害
这 个 距 离 就 是 p , 对 于 每 个 儿 子 将 多 余 的 影 响 减 去 这个距离就是\sqrt{p},对于每个儿子将多余的影响减去 p
每 个 儿 子 最 多 收 到 这 么 多 深 度 的 影 响 每个儿子最多收到这么多深度的影响
然 后 把 上 述 式 子 化 简 然后把上述式子化简
p − ( d j 2 − 2 ∗ d i ∗ d j + d i 2 ) p-(d_j^2-2*d_i*d_j+d_i^2) p(dj22didj+di2)
这 样 就 发 现 , 其 实 对 于 每 个 子 结 点 这样就发现,其实对于每个子结点
只 和 上 述 结 点 的 攻 击 次 数 , 深 度 和 深 度 的 平 方 有 关 系 只和上述结点的攻击次数,深度和深度的平方有关系
所 以 直 接 用 树 形 d p 维 护 这 些 值 所以直接用树形dp维护这些值 dp
但 是 由 于 刚 才 算 的 最 远 距 离 , 所 以 每 次 超 出 这 个 距 离 的 时 候 需 要 减 去 但是由于刚才算的最远距离,所以每次超出这个距离的时候需要减去
每 次 贪 心 对 尽 量 靠 上 的 结 点 进 行 攻 击 每次贪心对尽量靠上的结点进行攻击
最 后 计 算 攻 击 次 数 最后计算攻击次数
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
#define SZ(x) (int)x.size()
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[][2]={{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};

ll ans,n,p,lim;
vector<int> g[maxn];
ll s1[maxn],s2[maxn],s[maxn],cnt[maxn],a[maxn],tot[maxn];
void dfs(int u,int fa,ll d){
    if(d>lim){
        ll t=d-lim;
        s[u]-=cnt[t];
        s1[u]-=cnt[t]*t;
        s2[u]-=cnt[t]*t*t;
    }
    ll tmp=s[u]*(p-d*d)-s2[u]+2*d*s1[u];
    if(a[u]>=tmp)tot[u]=(a[u]-tmp)/p+1,ans+=tot[u];
    cnt[d]=tot[u];
    for(auto v:g[u]){
        if(v==fa)continue;
        s[v]=s[u]+tot[u];
        s1[v]=s1[u]+tot[u]*d;
        s2[v]=s2[u]+tot[u]*d*d;
        dfs(v,u,d+1);
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>n>>p;lim=sqrt(p-1)+1;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].pb(v);
        g[v].pb(u);
    }
    dfs(1,0,1);
    cout<<ans;
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值