Description
Input
Output
Sample Input
1 1 1 1 1
1 2
1 3
2 4
2 5
2 1
3 1
4 1
Sample Output
7
9
10
在修正开始之前,如果按照所在城市4,1,5,3,2的顺序崛起,那么依次会和0,1,2,1,2个
国家进行战争。这时一共会产生6对敌对关系。可以证明这是所有崛起顺序中的最大值。
题解Here!
正题:
先看没有修改怎么做。
我们发现点$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)$的。
附代码:
#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$替换掉就好辣!