最大权独立集问题
题解
相当有趣的一道dp题。
我考场上全花时间去搞前两题了,T3只rush了一个暴力,亏大分。
首先我们可以考虑将原来的边交换的条件转化一下。
相当于我们要给每一个点规定一条不经过重复点的有向路径,使得所有点的路径加起来可以覆盖满整棵树。
显然,我们很容易通过这得到一种
d
p
dp
dp的思路。
可以发现,从子树节点的父亲往子树内覆盖的路径只会有一条,从子树内向父亲传递的点也只会有一个。
所以我们
d
p
dp
dp时不妨就记录这个路径回到哪一个点,子树内向上传递又会传递哪一个点。
显然,在一个点上我们需要枚举的就是选择边的顺序,是先父边,先左儿子,还是先右儿子,通过这从底向上转移。
但这样的话我们的
d
p
dp
dp状态总数是会达到
n
3
n^3
n3的,貌似不太能过的样子。
但不要着急,显然,传递到的点与向上传递的点不会在一棵子树内,该状态只会出现在这两个点的
l
c
a
lca
lca处。
所以,我们的状态数实际上是
n
2
n^2
n2的。
但我们上面的
d
p
dp
dp状态是可以继续被优化的,因为最后被传递到的位置我们真正关心的只有它的深度,我们把这个深度记到状态里面即可。
好的,我们现在
d
p
u
,
x
,
d
dp_{u,x,d}
dpu,x,d表示我们现在对于点
u
u
u的子树内,我们将会把点
x
x
x传到它的父亲去,并且父亲传下来的点往下传长度为
d
d
d的路径。
通过考虑边交换的执行顺序,容易得到
d
p
dp
dp转移式:
d
p
u
,
u
,
d
1
=
d
p
l
s
o
n
,
x
,
d
1
−
1
+
d
p
r
s
o
n
,
y
,
d
2
+
(
d
2
+
2
)
v
a
l
x
+
v
a
l
y
d
p
u
,
u
,
d
1
=
d
p
r
s
o
n
,
x
,
d
1
−
1
+
d
p
l
s
o
n
,
y
,
d
2
+
(
d
2
+
2
)
v
a
l
x
+
v
a
l
y
d
p
u
,
x
,
d
1
=
d
p
l
s
o
n
,
x
,
d
2
+
d
p
r
s
o
n
,
y
,
d
1
−
1
+
(
d
2
+
1
)
v
a
l
u
+
v
a
l
y
+
v
a
l
x
d
p
u
,
x
,
d
1
=
d
p
r
s
o
n
,
x
,
d
2
+
d
p
l
s
o
n
,
y
,
d
1
−
1
+
(
d
2
+
1
)
v
a
l
u
+
v
a
l
y
+
v
a
l
x
d
p
u
,
x
,
0
=
d
p
l
s
o
n
,
y
,
d
1
+
d
p
r
s
o
n
,
x
,
d
2
+
(
d
1
+
1
)
v
a
l
u
+
(
d
2
+
2
)
v
a
l
y
+
v
a
l
x
d
p
u
,
x
,
0
=
d
p
r
s
o
n
,
y
,
d
1
+
d
p
l
s
o
n
,
x
,
d
2
+
(
d
1
+
1
)
v
a
l
u
+
(
d
2
+
2
)
v
a
l
y
+
v
a
l
x
dp_{u,u,d_1}=dp_{lson,x,d_1-1}+dp_{rson,y,d_2}+(d_2+2)val_x+val_y\\ dp_{u,u,d_1}=dp_{rson,x,d_1-1}+dp_{lson,y,d_2}+(d_2+2)val_x+val_y\\ dp_{u,x,d_1}=dp_{lson,x,d_2}+dp_{rson,y,d_1-1}+(d_2+1)val_u+val_y+val_x\\ dp_{u,x,d_1}=dp_{rson,x,d_2}+dp_{lson,y,d_1-1}+(d_2+1)val_u+val_y+val_x\\ dp_{u,x,0}=dp_{lson,y,d_1}+dp_{rson,x,d_2}+(d_1+1)val_u+(d_2+2)val_y+val_x\\ dp_{u,x,0}=dp_{rson,y,d_1}+dp_{lson,x,d_2}+(d_1+1)val_u+(d_2+2)val_y+val_x
dpu,u,d1=dplson,x,d1−1+dprson,y,d2+(d2+2)valx+valydpu,u,d1=dprson,x,d1−1+dplson,y,d2+(d2+2)valx+valydpu,x,d1=dplson,x,d2+dprson,y,d1−1+(d2+1)valu+valy+valxdpu,x,d1=dprson,x,d2+dplson,y,d1−1+(d2+1)valu+valy+valxdpu,x,0=dplson,y,d1+dprson,x,d2+(d1+1)valu+(d2+2)valy+valxdpu,x,0=dprson,y,d1+dplson,x,d2+(d1+1)valu+(d2+2)valy+valx总共
6
6
6个冗杂的方程,因为涉及到
l
s
o
n
lson
lson与
r
s
o
n
rson
rson的相对顺序,其实只有
3
3
3个本质不同的转移。
现在,如果我们直接按照这个
d
p
dp
dp转移式去转移的话,是
O
(
n
4
)
O\left(n^4\right)
O(n4)的,不太能过的样子。
但这明显是可以优化的。
比如第一个式子,我们发现,我们的
y
y
y只会贡献到
d
2
d_2
d2上,对其它值根本没有影响,所以我们可以对于每个
d
2
d_2
d2找到它选择的最优的
y
y
y,这个
d
2
d_2
d2之后再第一个转移中只会与这个
y
y
y匹配。
同样,再这之后,我们的
d
2
d_2
d2也只会被贡献到
x
x
x上,对于其他值根本没有影响,,所以我们再对于每个
x
x
x找到这个
d
2
d_2
d2。
最后,用同样的方式,把
x
x
x贡献到
d
1
d_1
d1上,每个
d
1
d_1
d1又会对应到唯一的
d
p
u
,
u
,
d
1
dp_{u,u,d_1}
dpu,u,d1,这样就完成我们的转移了。
后面的转移方程式都可以用类似的方法优化,这里就不多阐释了。
容易发现我们上面的转移复杂度的分析是套用树形
d
p
dp
dp的分析方法的,每个点在每个
l
c
a
lca
lca处转移时的深度都是它另一子树的深度。
总时间复杂度显然是
O
(
n
2
)
O\left(n^2\right)
O(n2)的。
源码
然而有点冗杂。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 5005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const LL INF=0x3f3f3f3f3f3f3f3f;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){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>
_T Fabs(_T x){return x<0?-x:x;}
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*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,val[MAXN],fp[MAXN],mxd[MAXN],ch[MAXN][2],deg[MAXN];
int sta[MAXN][MAXN],stak[MAXN];
LL f[MAXN],g[MAXN],h[MAXN],dp[MAXN][MAXN],tmp[MAXN][MAXN];
LL F[MAXN],Gf[MAXN],Gg[MAXN],ans;
void sakura(int rt){
sta[rt][++stak[rt]]=rt;int ls=ch[rt][0],rs=ch[rt][1];
if(!deg[rt]){dp[rt][0]=mxd[rt]=0;return ;}
if(ls)sakura(ls),h[rt]=max(h[rt],h[ls]+1);
if(rs)sakura(rs),h[rt]=max(h[rt],h[rs]+1);
for(int j=0;j<=n;j++)dp[rt][j]=INF;
if(deg[rt]==1){
for(int i=1;i<=stak[ls];i++){
int x=sta[ls][i];mxd[rt]=max(mxd[rt],mxd[x]+1);
for(int j=0;j<=mxd[x];j++)
tmp[x][j]=dp[x][j],dp[x][j]=INF;
for(int j=0;j<=mxd[x];j++)
dp[rt][j+1]=min(dp[rt][j+1],tmp[x][j]+val[x]),
dp[x][0]=min(dp[x][0],tmp[x][j]+1ll*(j+1)*val[rt]+val[x]);
sta[rt][++stak[rt]]=x;mxd[x]=0;
}
return ;
}
for(int i=0;i<=h[ls];i++)f[i]=Gf[i]=INF;
for(int i=0;i<=h[rs];i++)g[i]=Gg[i]=INF;
for(int i=1;i<=stak[ls];i++){
int x=sta[ls][i];sta[rt][++stak[rt]]=x;
for(int j=0;j<=mxd[x];j++)
f[j]=min(f[j],val[x]+(tmp[x][j]=dp[x][j])),dp[x][j]=INF;
}
for(int i=1;i<=stak[rs];i++){
int x=sta[rs][i];sta[rt][++stak[rt]]=x;
for(int j=0;j<=mxd[x];j++)
g[j]=min(g[j],val[x]+(tmp[x][j]=dp[x][j])),dp[x][j]=INF;
}
for(int i=1;i<=stak[ls];i++){
int x=sta[ls][i];LL minn=F[x]=INF,mn=INF;
for(int j=0;j<=h[rs];j++)
minn=min(minn,g[j]+1ll*val[x]*(j+2));
for(int j=0;j<=mxd[x];j++)
dp[rt][j+1]=min(dp[rt][j+1],minn+tmp[x][j]),
mn=min(mn,tmp[x][j]+1ll*(j+1)*val[rt]);
minn=INF;
for(int j=0;j<=mxd[x];j++)
minn=min(minn,tmp[x][j]+1ll*val[rt]*(j+1));
for(int j=0;j<=h[rs];j++)
dp[x][j+1]=min(dp[x][j+1],minn+g[j]+val[x]),
Gg[j]=min(Gg[j],1ll*(j+2)*val[x]+mn);
}
for(int i=1;i<=stak[rs];i++){
int x=sta[rs][i];LL minn=INF,mn=INF;
for(int j=0;j<=h[ls];j++)
minn=min(minn,f[j]+1ll*val[x]*(j+2));
for(int j=0;j<=mxd[x];j++)
dp[rt][j+1]=min(dp[rt][j+1],minn+tmp[x][j]),
mn=min(mn,tmp[x][j]+1ll*(j+1)*val[rt]);
minn=INF;
for(int j=0;j<=mxd[x];j++)
minn=min(minn,tmp[x][j]+1ll*val[rt]*(j+1));
for(int j=0;j<=h[ls];j++)
dp[x][j+1]=min(dp[x][j+1],minn+f[j]+val[x]),
Gf[j]=min(Gf[j],1ll*(j+2)*val[x]+mn);
}
for(int i=1;i<=stak[ls];i++){
int x=sta[ls][i];LL minn=INF;
for(int j=0;j<=mxd[x];j++)minn=min(minn,tmp[x][j]+Gf[j]),tmp[x][j]=INF;
dp[x][0]=min(dp[x][0],minn+val[x]);mxd[x]=h[rs]+1;
}
for(int i=1;i<=stak[rs];i++){
int x=sta[rs][i];LL minn=INF;
for(int j=0;j<=mxd[x];j++)minn=min(minn,tmp[x][j]+Gg[j]),tmp[x][j]=INF;
dp[x][0]=min(dp[x][0],minn+val[x]);mxd[x]=h[ls]+1;
}
mxd[rt]=h[rt];
}
int main(){
read(n);ans=INF;
for(int i=1;i<=n;i++)read(val[i]);
for(int i=2,x;i<=n;i++)
read(x),ch[x][deg[x]++]=i;
for(int i=1;i<=n;i++)
for(int j=0;j<=n;j++)
tmp[i][j]=dp[i][j]=INF;
sakura(1);
for(int i=1;i<=n;i++)ans=min(ans,dp[i][0]);
printf("%lld\n",ans);
return 0;
}