D. Geniue
Frozen_Guardian题解
Implicit_总结
首先把此序列看作一个完全图,然后按照边权从小到大的顺序枚举边。
如何按照边权从小到大枚举边?
下面考虑形如边(a,b)(a,b)(a,b)都默认a<ba<ba<b。
任意考虑两条边(a,b)(a,b)(a,b)和(c,d)(c,d)(c,d),不难发现只要有b<db<db<d,一定有ca,b<cc,dc_{a,b}<c_{c,d}ca,b<cc,d,而对于较大的点相同的情况比如(a,i)(a,i)(a,i)和(b,i)(b,i)(b,i),显然如果a<ba<ba<b那么有边权ca,i>cb,ic_{a,i}>c_{b,i}ca,i>cb,i。
于是如果想要从小到大枚举边权只需要从小到大枚举iii,从大到小枚举jjj
设计dp:
状态表示:fif_ifi表示以iii节点结尾时的最大值
状态转移:{fi=max{fi,fj+∣si−sj∣}fj=max{fj,fi+∣si−sj∣}\begin{cases}f_i=\max\{f_i,f_j+|s_i-s_j|\}\\f_j=\max\{f_j,f_i+|s_i-s_j|\}\end{cases}{fi=max{fi,fj+∣si−sj∣}fj=max{fj,fi+∣si−sj∣}
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
using ll=long long;
constexpr int N=200010;
ll dp[N],s[N],tag[N];
int n;
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);int T=1;cin>>T;while(T--){cin>>n;for(int i=1;i<=n;i++) cin>>tag[i];for(int i=1;i<=n;i++) cin>>s[i];memset(dp,0,sizeof(ll)*(n+1));// i从小打到枚举 而i相同从大到小枚举j 保证边是从小到大枚举 本质枚举边for(int i=1;i<=n;i++)for(int j=i-1;j>=1;j--)if(tag[i]!=tag[j]){ll dpj=dp[i]+abs(s[i]-s[j]);ll dpi=dp[j]+abs(s[i]-s[j]);dp[i]=max(dp[i],dpi);dp[j]=max(dp[j],dpj);}cout<<*max_element(dp+1,dp+1+n)<<'\n';}return 0;
}