中心城镇问题
题解
实质上是一个非常经典的长链剖分dp题。
我们可以记
d
p
u
,
i
dp_{u,i}
dpu,i表示在以点
u
u
u为根节点的子树内,最深的特殊点深度为
i
i
i时总的权值最大值。
我们的转移主要分两个部分,合并两棵子树,与将当前子树的根节点赋为特殊点。
对于第一个转移,我们需要保证合并的两棵子树它们最浅的特殊点的距离是不小于
K
K
K的,转移式为
d
p
u
,
i
=
min
min
(
j
,
k
)
=
i
∧
j
+
k
−
2
d
e
p
u
⩾
K
d
p
u
,
j
+
d
p
v
,
k
dp_{u,i}=\min_{\min(j,k)=i\wedge j+k-2dep_u\geqslant K}dp_{u,j}+dp_{v,k}
dpu,i=min(j,k)=i∧j+k−2depu⩾Kmindpu,j+dpv,k但这样需要枚举
j
,
k
j,k
j,k两个,显然是会
T
T
T飞的,但我们观察到上面的状态是可以通过后缀最大值优化的,我们将
d
p
u
,
i
dp_{u,i}
dpu,i的定义换成在以点
u
u
u为根的子树内,最深的特殊点深度不小于
i
i
i时总权值的最大值。
d
p
u
,
i
=
min
(
d
p
u
,
i
+
d
p
v
,
d
e
p
u
+
K
−
i
,
d
p
v
,
i
+
d
p
u
,
d
e
p
u
+
K
−
i
)
dp_{u,i}=\min(dp_{u,i}+dp_{v,dep_u+K-i},dp_{v,i}+dp_{u,dep_u+K-i})
dpu,i=min(dpu,i+dpv,depu+K−i,dpv,i+dpu,depu+K−i)但这样实际上转移后并没有真正使得转移后的
d
p
u
,
i
dp_{u,i}
dpu,i表示上个定义
d
p
u
,
i
dp_{u,i}
dpu,i的后缀最大值,由于它限制了
j
j
j是被累积到
i
i
i上的,势必会使得该
j
j
j的转移范围减少,也就是说存在
j
+
k
⩾
K
+
2
d
e
p
u
>
i
+
K
j+k\geqslant K+2dep_u>i+K
j+k⩾K+2depu>i+K的情形。
面对这种情况,我们不妨在转移后再做一次后缀最小值,显然在合并这两棵子树后,如果
v
v
v中有特殊点,即有贡献的话,我们影响到的
d
p
u
,
i
dp_{u,i}
dpu,i的
i
i
i一定是不会超过
v
v
v子树中最深节点的深度的。故我们做的后缀最大值的时间复杂度与前面转移的时间复杂度都是
O
(
max
v
′
∈
T
v
d
e
p
v
)
O\left(\max_{v'\in T_v}dep_v\right)
O(maxv′∈Tvdepv)的,即最长链长度。
既然如此,我们就可以联想到通过长链剖分来优化我们的
d
p
dp
dp转移,每次把短的链合并到长的链上。
第二部分的转移就是再将我们当前的
u
u
u赋为特殊点,显然这种情况需要保证已有的特殊点都与
u
u
u距离不小于
K
K
K,即深度都不小于
d
e
p
u
+
K
dep_u+K
depu+K,我们的
d
p
dp
dp又是维护的后缀最大值,有转移式,
d
p
u
,
d
e
p
u
=
v
a
l
u
+
d
p
u
,
d
e
p
u
+
K
dp_{u,dep_u}=val_u+dp_{u,dep_u+K}
dpu,depu=valu+dpu,depu+K这种转移显然是
O
(
1
)
O\left(1\right)
O(1)的,它现在是在最前面的,并不会影响其它的后缀。
整个就是一个长链剖分优化 d p dp dp的过程,每条链只会产生一次贡献,之后就被并到长链上了,以后就是长链产生贡献,是 O ( n ) O\left(n\right) O(n)的。不过重链剖分也可以过,可能时间复杂度与空间复杂度不大一样。
时间复杂度 O ( n ) O\left(n\right) O(n)(长链)或 O ( n log n ) O\left(n\log\,n\right) O(nlogn)(重链)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int mod=1e5+3;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=2000;
const int n1=1000;
const int M=100000;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,K,val[MAXN],dep[MAXN],head[MAXN],tot;
int siz[MAXN],wson[MAXN],id[MAXN],mxd[MAXN],stak;
LL f[22][MAXN],g[MAXN],ans;
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
void dosaka1(int u,int fa){
siz[u]=1;dep[u]=dep[fa]+1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa)continue;
dosaka1(v,u);siz[u]+=siz[v];mxd[u]=max(mxd[u],mxd[v]);
if(siz[v]>siz[wson[u]])wson[u]=v;
}
}
void dosaka2(int u,int fa){
mxd[u]=dep[u];if(wson[u])id[wson[u]]=id[u],dosaka2(wson[u],u),mxd[u]=mxd[wson[u]];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(v==fa||v==wson[u])continue;
id[v]=++stak;dosaka2(v,u);
for(int j=dep[u]+1;j<=min(dep[u]+K,mxd[v]);j++){
int k=max(j,dep[u]+dep[u]+K-j);
if(k<=mxd[v])g[j]=max(f[id[u]][j]+f[id[v]][k],g[j]);else g[j]=max(f[id[u]][j],g[j]);
if(k<=mxd[u])g[j]=max(f[id[v]][j]+f[id[u]][k],g[j]);else g[j]=max(f[id[v]][j],g[j]);
}
for(int j=min(mxd[v],dep[u]+K);j>dep[u];j--)
f[id[u]][j]=max(f[id[u]][j+1],g[j]),g[j]=f[id[v]][j]=0;
mxd[u]=max(mxd[v],mxd[u]);stak--;
}
f[id[u]][dep[u]]=(LL)val[u];
if(mxd[u]>=dep[u]+K)f[id[u]][dep[u]]+=f[id[u]][dep[u]+K],f[id[u]][dep[u]+K]=0;
f[id[u]][dep[u]]=max(f[id[u]][dep[u]],f[id[u]][dep[u]+1]);
}
signed main(){
//freopen("central.in","r",stdin);
//freopen("central.out","w",stdout);
read(n);read(K);for(int i=1;i<=n;i++)read(val[i]);K++;
for(int i=1;i<n;i++){
int u,v;read(u);read(v);
addEdge(u,v);addEdge(v,u);
}
dosaka1(1,0);id[1]=++stak;dosaka2(1,0);
for(int i=1;i<=min(mxd[1],K);i++)ans=max(ans,f[id[1]][i]);
printf("%lld\n",ans);
return 0;
}