Codeforces Round #701 (Div. 2) E. Move and Swap

题目链接:

https://codeforces.com/problemset/problem/1485/E

题解:

dp。
注意到在每一层中,蓝币的位置是任意的,而红币的位置是有限制的,仅能由父节点转移到儿子节点。
考虑从上到下一层一层地转移,令dp[i]表示红币位于i号节点的最大答案,j表示i号节点的父亲,当我们不考虑换位时,显然:

[dp[i]=dp[j]+max(|a_i-a_b|) ]

其中(ab表示与i位于同一层的蓝币所处位置的值)。我们确定红币位置后,直接贪心取最大的|ai-ab|即可
现考虑换位,则i可以是本层中任意一个位置(因为蓝币在本层中位置是任意的,你可以放好蓝币后与红币交换)。设换位前红币位于k,s是k的父亲,则:

[dp[i]=max(dp[i],dp[s]+|a_i-a_k|) ]

这样,对于每一个i,我们在这一层中枚举所有的位置取最优即可
然而这样在每层中,每次更新是(O(n^2))的,全局复杂度也是(O(n^2))。现考虑如何优化:
当ai>ak时,去掉绝对值,得:

[dp[s]+|a_i-a_k|=dp[s]+a_i-a_k ]

我们注意到,处理完上一层后,对于位置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
这样,状态转移方程为:

[dp[i]=max(dp[i],pamx[i]+a_i,bmax[i]-a_i) ]

最后,我们考虑如何实现一层一层地枚举点。我是把所有点以深度为关键字排序,这样相同深度的点的下标就是一个连续的子段了,令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
",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
*/
小鳥の翼がついに大きくなって , 旅立ちの日だよ , 遠くへと広がる海の色暖かく , 夢の中で描いた絵のようなんだ , 切なくて時をまきもどしてみるかい ? No no no いまが最高! だってだって、いまが最高!
原文地址:https://www.cnblogs.com/nanjolno/p/14556436.html