Codeforces Round #701 (Div. 2) E. Move and Swap
题目链接:
https://codeforces.com/problemset/problem/1485/E
题解:
dp。
注意到在每一层中,蓝币的位置是任意的,而红币的位置是有限制的,仅能由父节点转移到儿子节点。
考虑从上到下一层一层地转移,令dp[i]表示红币位于i号节点的最大答案,j表示i号节点的父亲,当我们不考虑换位时,显然:
其中(ab表示与i位于同一层的蓝币所处位置的值)。我们确定红币位置后,直接贪心取最大的|ai-ab|即可
现考虑换位,则i可以是本层中任意一个位置(因为蓝币在本层中位置是任意的,你可以放好蓝币后与红币交换)。设换位前红币位于k,s是k的父亲,则:
这样,对于每一个i,我们在这一层中枚举所有的位置取最优即可
然而这样在每层中,每次更新是\(O(n^2)\)的,全局复杂度也是\(O(n^2)\)。现考虑如何优化:
当ai>ak时,去掉绝对值,得:
我们注意到,处理完上一层后,对于位置k,dp[s]-ak的值可以预处理。同理,当ai<ak时,dp[s]+ak的值也可以预处理。
在每一层中,我们把当前层的点用一个vector来维护。我们对当前层的元素以ai为关键字升序排序,这样,处理第i个元素时,对于其前面的元素,我们取最大的dp[s]-ak,对于其后面的元素,我们取最大的dp[s]+ak。为了实现这点,我们考虑构造一个前缀数组pmax和后缀数组bmax来维护其最值,pmax[i]表示i前面所有元素中最大的dp[s]-ak,bmax[i]表示i后面所有元素中最大的dp[s]+ak
这样,状态转移方程为:
最后,我们考虑如何实现一层一层地枚举点。我是把所有点以深度为关键字排序,这样相同深度的点的下标就是一个连续的子段了,令L[i]表示深度为i的段的首端,R[i]为末端,在dp前处理出L[i],R[i]即可
代码如下:
注意我这里使用bmax维护的前缀,pmax维护的后缀
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
struct front_star{
int to,next;
}e[400005];
struct poi{
int id,fa,d;
long long val;
}node[200005];
int T,n,cnt=0,mxd=0;
long long ans=0;
int head[200005],L[200005],R[200005];
long long dp[200005],tpf[200005],tps[200005],pmx[200005],bmx[200005];
vector<poi>f;
inline int read()
{
int X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
inline long long maxn(long long a,long long b)
{
return a>b?a:b;
}
inline void addedge(int u,int v)
{
cnt++;
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
inline bool wcnmp(poi a,poi b)
{
if(a.d<b.d)
return true;
return false;
}
inline void dfs(int u,int deep)
{
mxd=maxn(deep,mxd);
node[u].d=deep;
for(register int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if(v!=node[u].fa)
{
node[v].fa=node[u].id;
dfs(v,deep+1);
}
}
}
inline bool cmp(poi a,poi b)
{
if(a.val<b.val)
return true;
return false;
}
int main()
{
scanf("%d",&T);
while(T--)
{
cnt=0;
ans=0;
mxd=0;
memset(head,-1,sizeof(head));
scanf("%d",&n);
for(register int i=2;i<=n;i++)
{
int s;
s=read();
addedge(i,s);
addedge(s,i);
}
node[1].id=1;
node[1].val=0;
for(register int i=2;i<=n;i++)
{
node[i].id=i;
node[i].val=read();
}
node[1].fa=0;
dfs(1,1);
sort(node+1,node+1+n,wcnmp);
L[1]=1,R[mxd]=n;
int now=1;
for(register int i=1;i<=n;i++)
{
if(now!=node[i].d)
{
R[now]=i-1;
now++;
L[now]=i;
}
}
memset(dp,0,sizeof(dp));
for(register int i=2;i<=mxd;i++)
{
f.clear();
for(register int j=L[i];j<=R[i];j++)
f.push_back(node[j]);
int upp=f.size();
sort(f.begin(),f.end(),cmp);
for(register int j=0;j<upp;j++)
{
bmx[j]=0;
pmx[j]=0;
tpf[j]=dp[f[j].fa]+f[j].val;
tps[j]=dp[f[j].fa]-f[j].val;
}
bmx[0]=tps[0];
pmx[upp-1]=tpf[upp-1];
for(register int j=1;j<upp;j++)
bmx[j]=maxn(bmx[j-1],tps[j]);
for(register int j=upp-2;j>=0;j--)
pmx[j]=maxn(pmx[j+1],tpf[j]);
for(register int j=0;j<upp;j++)
{
poi tp=f[j];
dp[tp.id]=maxn(dp[tp.id],dp[tp.fa]+f[upp-1].val-tp.val);
dp[tp.id]=maxn(dp[tp.id],dp[tp.fa]-f[0].val+tp.val);
dp[tp.id]=maxn(dp[tp.id],bmx[j]+tp.val);
dp[tp.id]=maxn(dp[tp.id],pmx[j]-tp.val);
}
}
for(register int i=L[mxd];i<=R[mxd];i++)
ans=maxn(dp[node[i].id],ans);
printf("%lld\n",ans);
}
return 0;
}
/*
1
17
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
141742934 542155364 408860514 496518420 268022205 252909000 521914080 315254896 636214743 875364320 404786741 620328680 510764873 126572193 547542158 951490057
*/