牛客練習賽28 頹紅警
阿新 • • 發佈:2018-12-14
題意:一顆有根樹,每個節點有一權值,為敵人的戰鬥力,你的戰鬥力為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; }