九條可憐是一個熱愛閱讀的女孩子。
這段時間,她看了一本很是有趣的小說,這本小說的架空世界引發了她的興趣。
這個世界有n個城市,這n個城市被剛好n?1條雙向道路聯通,即任意兩個城市均可以互相到達。同時城市1坐落在世
界的中心,佔領了這個城市就稱霸了這個世界。
在最開始,這n個城市都不在任何國家的控制之下,可是隨着社會的發展,一些城市會崛起造成國家並奪取世界的
霸權。爲了方便,咱們標記第i個城市崛起產生的國家爲第i個國家。在第i個城市崛起的過程當中,第i個國家會取得
城市i到城市1路徑上全部城市的控制權。
新的城市的崛起每每意味着戰爭與死亡,若第i個國家在崛起中,須要取得一個本來被國家j(j!=i)控制的城市的控
制權,那麼國家i就必須向國家j宣戰並進行戰爭。
如今,可憐知道了,在歷史上,第i個城市一共崛起了ai次。可是這些事件發生的相對順序已經無從考究了,惟一
的信息是,在一個城市崛起稱霸世界以前,新的城市是不會崛起的。戰爭對人民來講是災難性的。可憐定義一次崛
起的災難度爲崛起的過程當中會和多少不一樣的國家進行戰爭(和同一個國家進行屢次戰爭只會被計入一次)。可憐想
要知道,在全部可能的崛起順序中,災難度之和最大是多少。
同時,在考古學家的努力下,愈來愈多的歷史資料被髮掘了出來,根據這些新的資料,可憐會對ai進行一些修正。
具體來講,可憐會對ai進行一些操做,每次會將ax加上w。她但願在每次修改以後,都能計算獲得最大的災難度。
然而可憐對複雜的計算並不感興趣,所以她想讓你來幫她計算一下這些數值。
對題面的一些補充:
1:同一個城市屢次崛起造成的國家是同一個國家,這意味着同一個城市連續崛起兩次是不會和任何國家開戰的:因
爲這些城市原來就在它的控制之下。
2:在歷史的演變過程當中,第i個國家可能會有一段時間沒有任何城市的控制權。可是這並不意味着第i個國家滅亡了
,在城市i崛起的時候,第i個國家仍然會取得1到i路徑上的城市的控制權
第一行輸入兩個整數n,m表示城市個數和操做個數。
第二行輸入n個整數表示ai的初始值。
接下來n-1行,每行輸入兩個整數ui,vi(1<=ui,vi<=n)描述了一條道路。
接下來m行每行輸入兩個整數xi,wi表示將axi加上wi
N,M<=4*10^5
1 <= ai, wi <= 10^7, 1 <= xi <= n
輸出共m+1行,第一行表示初始的ai的答案,接下來m行每行表示此次修正後的答案
先看沒有修改怎麼作。
咱們發現點$i$會開戰當且僅當$i$的子樹裏有城市崛起,而且和上次崛起的城市不一樣。
設點$u$有$m$個兒子,$u$本身$access$次數爲$A_0$,第$i$個兒子的子樹的$access$次數爲$A_i$。
問題轉化爲有$m+1$個不一樣顏色的小球,每種有$A_i$個,求一種排列使得相鄰顏色不一樣的個數最多。
設$$sum=\sum\limits_{i=0}^{m}A_i,mx=\max\limits_{i=0}^{m}A_i$$
則點$u$最大切換次數就是$\min(sum-1,2\times(sum-mx))$。
能夠看出每個點都只和本身的子樹有關,也就是說兩點之間的貢獻都是相互獨立的。
那麼咱們就能夠用樹形$DP$求一次靜態的答案了。
這樣就有$30$分。
而後考慮帶上修改。
咱們只須要動態地維護$DP$值便可對吧。
很天然的想到了樹形數據結構:樹鏈剖分/$LCT$。
這裏咱們用$LCT$來維護。
設$sum_u$表示$u$子樹裏的$A_i$之和。
若是$\exists v \in son[u],sum_u+1 \leq 2sum_v$,則$u,v$之間連實邊,不然爲虛邊。
顯然這樣的$v$只有一個。
修改時在$LCT$上把虛邊所有修改一次便可。
能夠證實虛邊的數量是$\log_2\sum A_i$的,和樹鏈剖分的證實方法相似。
因此複雜度是$O(n\log_2n)$的。ios
附代碼:c++
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 500010
using namespace std;
int n,m,c=1;
long long ans=0;
int head[MAXN];
struct Edge{
int next,to;
}edge[MAXN<<1];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline long long min(const long long x,const long long y){return x<y?x:y;}
namespace LCT{
struct Link_Cut_Tree{
int f,son[2];
long long v,w,sum;
}a[MAXN];
inline bool isroot(int rt){
return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
}
inline void pushup(int rt){
if(!rt)return;
a[rt].sum=a[a[rt].son[0]].sum+a[a[rt].son[1]].sum+a[rt].v+a[rt].w;
}
inline void turn(int rt){
int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
if(!isroot(x)){
if(a[y].son[0]==x)a[y].son[0]=rt;
else a[y].son[1]=rt;
}
a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
pushup(x);pushup(rt);
}
void splay(int rt){
while(!isroot(rt)){
int x=a[rt].f,y=a[x].f;
if(!isroot(x)){
if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
else turn(x);
}
turn(rt);
}
}
void access(int rt,int k,int after){
for(;rt;after=rt,rt=a[rt].f){
splay(rt);
long long sum=a[a[rt].son[1]].sum+a[rt].v+a[rt].w;
if(a[rt].son[1])ans-=2LL*(sum-a[a[rt].son[1]].sum);
else if(sum+1<=2LL*a[rt].v)ans-=2LL*(sum-a[rt].v);
else ans-=sum-1;
a[rt].sum+=k;a[rt].w+=k;sum+=k;
if(sum+1>2LL*a[a[rt].son[1]].sum){
a[rt].w+=a[a[rt].son[1]].sum;
a[rt].son[1]=0;
}
if(sum+1<=2LL*a[after].sum){
a[rt].son[1]=after;
a[rt].w-=a[a[rt].son[1]].sum;
}
if(a[rt].son[1])ans+=2LL*(sum-a[a[rt].son[1]].sum);
else if(sum+1<=2LL*a[rt].v)ans+=2LL*(sum-a[rt].v);
else ans+=sum-1;
}
}
void update(int rt,long long k){
splay(rt);
long long sum=a[a[rt].son[1]].sum+a[rt].v+a[rt].w;
if(a[rt].son[1])ans-=2LL*(sum-a[a[rt].son[1]].sum);
else if(sum+1<=2LL*a[rt].v)ans-=2LL*(sum-a[rt].v);
else ans-=sum-1;
a[rt].v+=k;a[rt].sum+=k;sum+=k;
if(sum+1>2LL*a[a[rt].son[1]].sum){
a[rt].w+=a[a[rt].son[1]].sum;
a[rt].son[1]=0;
}
if(a[rt].son[1])ans+=2LL*(sum-a[a[rt].son[1]].sum);
else if(sum+1<=2LL*a[rt].v)ans+=2LL*(sum-a[rt].v);
else ans+=sum-1;
access(a[rt].f,k,rt);
}
void dfs(int rt,int fa){
int maxx=rt;
long long maxn=a[rt].v;
a[rt].f=fa;
a[rt].sum=a[rt].v;
for(int i=head[rt],will;i;i=edge[i].next){
will=edge[i].to;
if(will!=fa){
dfs(will,rt);
a[rt].sum+=a[will].sum;
if(a[will].sum>maxn){
maxn=a[will].sum;
maxx=will;
}
}
}
ans+=min(a[rt].sum-1,2LL*(a[rt].sum-maxn));
if(maxx!=rt&&a[rt].sum+1<=2LL*maxn)a[rt].son[1]=maxx;
a[rt].w=a[rt].sum-a[rt].v-a[a[rt].son[1]].sum;
}
}
inline void add(int x,int y){
edge[c].to=y;edge[c].next=head[x];head[x]=c++;
edge[c].to=x;edge[c].next=head[y];head[y]=c++;
}
void work(){
int x,y;
while(m--){
x=read();y=read();
LCT::update(x,y);
printf("%lld\n",ans);
}
}
void init(){
int x,y;
n=read();m=read();
for(int i=1;i<=n;i++)LCT::a[i].v=read();
for(int i=1;i<n;i++){
x=read();y=read();
add(x,y);
}
LCT::dfs(1,0);
printf("%lld\n",ans);
}
int main(){
init();
work();
return 0;
}
$UPDATE$:這個程序在各大$OJ$上均能經過,只有洛谷可能不行。
stack<int> one,two;
void dfs(){
int rt=1;
a[rt].f=0;
one.push(rt);two.push(rt);
while(!one.empty()){
rt=one.top();
one.pop();
a[rt].sum=a[rt].v;
for(int i=head[rt],will;i;i=edge[i].next){
will=edge[i].to;
if(will==a[rt].f)continue;
a[will].f=rt;
one.push(will);two.push(will);
}
}
while(!two.empty()){
rt=two.top();
two.pop();
long long maxn=a[rt].v,maxx=rt;
for(int i=head[rt],will;i;i=edge[i].next){
will=edge[i].to;
if(will==a[rt].f)continue;
a[rt].sum+=a[will].sum;
if(a[will].sum>maxn){
maxn=a[will].sum;
maxx=will;
}
}
ans+=min(a[rt].sum-1,2LL*(a[rt].sum-maxn));
if(maxx!=rt&&a[rt].sum+1<=2LL*maxn)a[rt].son[1]=maxx;
a[rt].w=a[rt].sum-a[rt].v-a[a[rt].son[1]].sum;
}
}
用這個把上面那個舊的$dfs$替換掉就好辣!數據結構