1. 程式人生 > >牛客練習賽28 頹紅警

牛客練習賽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;
}