[CTS2022] 独立集问题

题目描述

小 E 喜欢出最大权独立集问题。

接下来,他还想了 nn 道最大权独立集问题。

但是和之前不一样,这回他想把 nn 道题整合成一道题出出来。

小 E 有 nn 个 AI,编号为 1\sim n1∼n 。

开始第 ii 个 AI 里面存有一道小 E 事先出好的一道难度为 d_idi​ 的最大权独立集问题。

有些 AI 之间可以互相通信,对于所有的 2 \le i \le n2≤i≤n ,第 ii 个 AI 可以和第 c_ici​ 个 AI 互相通信。其中 c_i<ici​<i。所以,这些 AI 连成了一个树的形状。此外,其他对 AI 不可以互相通信。

小 E 每次可以选择一个存有一道最大权独立集问题的 AI,结合它能够直接互相通信的所有 AI 中的问题,变成一道新的最大权独立集问题。整合问题时总有难度损失,不可能是把所有问题的难度直接相加。这道新的最大权独立集问题的难度是那些能够直接互相通信的 AI 中的题目难度和减去原本存在该 AI 上的问题的难度。然后那些能够直接互相通信的 AI 中的问题难度会变成 00。

小 E 希望通过若干次操作,把 n-1n−1 个 AI 中的题目难度都变成 00,然后把最后一个 AI 中的题目出出来。

由于出题人的丧病心理,小 E 想要出出来的最大权独立集问题的难度尽量大。

他想叫你帮他解决这个问题,还说如果你成功解决了这个问题,那么在出那道最大权独立集问题的时候,他会帮你提交一份标程代码。

输入格式

第一行一个正整数 nn。

第二行 nn 个整数,第 ii 个表示 d_idi​。

第三行 n-1n−1 个正整数,第 ii 个表示 c_{i+1}ci+1​。

输出格式

一行一个整数表示答案。

输入输出样例

输入 #1   copy

4
-1 2 3 4
1 1 1

输出 #1  copy

10

说明/提示

保证 1\le n\le 3514931≤n≤351493。

保证 1\le c_i <i1≤ci​<i。

保证 |d_i|\le 10^9∣di​∣≤109。

子任务 1(13 分):c_i=i-1ci​=i−1。

子任务 2(6 分):d_i>0di​>0。

子任务 3(11 分):n\le 7n≤7。

子任务 4(13 分):n\le 16n≤16。

子任务 5(22 分):n\le 100n≤100。

子任务 6(35 分):无特殊性质。

心动的阅读体验

题目链接

来自 2023SDPT-Round1-Day4 课上 Qingyu 的讲解。

考虑对于一个点多次操作会发生什么?第一次操作会将周围的点的权值吸过来,自己对答案的贡献乘 -1−1,周围的点的贡献乘 +1+1,得到新的权值 a_x' = \pm a_x \mp \sum_{y \in son_x} a_yax′​=±ax​∓∑y∈sonx​​ay​;再一次操作,我们可以将这个新的贡献取反。于是我们可以得到如果一个点被操作,那么他最后的贡献可以自由控制符号,答案求的是最大值,|a_i'|∣ai′​∣ 的绝对值号可以拆开算贡献。

来点暴力 DP。很自然能够想到分成三种进行讨论:自己的值被父亲吸走,自己的值被儿子吸走,自己吸走别人的值。那么不妨设 f_{x,0/1/2,0/1}fx,0/1/2,0/1​ 记录,第一维是节点编号,第二维是三种情况讨论,第三维是当前节点是否吸走了父亲的值,DP 的值即为按这种方式转移能够得到的最大答案。接下来考虑如何转移:

  • 首先我们钦定如果是 f_{x,0,0/1}fx,0,0/1​ 即被父亲吸走后,依旧可以操控这个状态来继续吸取儿子;如果是 f_{x,1,0/1}fx,1,0/1​ 即被某个儿子吸走后,依旧可以操控这个状态来吸取父亲和其他儿子;
  • 能够注意到 f_{x,0,1}fx,0,1​ 是没有意义的,因为不可能自己的值被父亲吸走,又吸走了父亲的值。这一维恒为 00;
  • 记 A_xAx​ 为 f_{son_x,0,0} \pm val_{son_x},f_{son_x,1,0},f_{son_x,2,0}fsonx​,0,0​±valsonx​​,fsonx​,1,0​,fsonx​,2,0​ 中的最大值,B_xBx​ 为 f_{son_x,0,0} \pm val_{son_x},f_{son_x,1,0}fsonx​,0,0​±valsonx​​,fsonx​,1,0​,C_xCx​ 为 f_{son_x,1,1},f_{son_x,2,1}fsonx​,1,1​,fsonx​,2,1​,其中 f_{son_x,0,0} \pm val_xfsonx​,0,0​±valx​ 并不是对两种运算去最值,而是对应第一段中那个式子的两种情况;
  • 对于 f_{x,0,0}fx,0,0​ 即被父亲吸了,那么儿子对转移过来的值的贡献就是每个儿子的最优值,即 \sum A_{son_x}∑Asonx​​;
  • 对于 f_{son_x,1,0/1}fsonx​,1,0/1​ 即被儿子吸了的,我们在决策被哪一个儿子吸收的时候,贪心的看一下不加那个儿子的权值最优即可,去掉的儿子即为 A_{son_x} - C_{son_x}Asonx​​−Csonx​​ 最小的那个,将该值即为 D_iDi​,儿子对转移过来的值的贡献即为 \sum A_{son_x} - D_i∑Asonx​​−Di​。吸了父亲的情况再加上对应情况的父亲的权值即可;
  • 对于 f_{son_x,2,0/1}fsonx​,2,0/1​ 即自己吸收的,儿子对转移过来的值的贡献即为 \sum B_{son_x}∑Bsonx​​,最后还要对应的加或减去自身的权值。吸了父亲的情况再加上对应情况的父亲的权值即可;
  • 对于每个点,分别记录一遍两种情况的 A_x,B_x,C_xAx​,Bx​,Cx​,再加上对应计算方式(第一段中的式子)的本身节点的权值,看一看选取哪个最优;
  • 最后的答案即为 \max{(f_{root,1,0},f_{root,2,0})}max(froot,1,0​,froot,2,0​)。

由此我们只需要遍历树一遍,记录和转移 DP 数组,细节还是很多的。时间复杂度为 \mathcal O(n)O(n)。

upd 2023.7.23 感谢 @trsins,修改了两个错误的式子。

#include<bits/stdc++.h>
#define ld long double 
#define ull unsigned long long
#define int long long
#define pb push_back
#define mp make_pair
using namespace std;

inline int read()
{
	int s=0,w=1; char c=getchar();
	while(!isdigit(c)) {if(c=='-') w=-1; c=getchar();}
	while(isdigit(c)) s=(s<<1)+(s<<3)+(c^48),c=getchar();
	return s*w;
}
inline void write(int x,char ch)
{
	if(x<0) x=-x,putchar('-');
	static char stk[25]; int top=0;
	do {stk[top++]=x%10+'0',x/=10;} while(x);
	while(top) putchar(stk[--top]);
	putchar(ch);
	return;
}

namespace MyTool
{
	static const int Mod=1e9+7;
	template<typename T> inline void Swp(T &a,T &b) {T t=a;a=b;b=t;}
	inline int  Max(int a,int b)   {return (b&((a-b)>>63))|(a&(~(a-b)>>63));}
	inline int  Min(int a,int b)   {return (a&((a-b)>>63))|(a&(~(a-b)>>63));}
	template<typename T> inline void cmax(T &a,T b) {a=a>b?a:b;}
	template<typename T> inline void cmin(T &a,T b) {a=a<b?a:b;}
	inline int  Abs(int a) {return (a^(a>>63))-(a>>63);}
	inline void Madd(int &a,int b) {a=a+b>Mod?a+b-Mod:a+b;}
	inline void Mdel(int &a,int b) {a=a-b<0?a-b+Mod:a-b;}
	inline void Mmul(int &a,int b) {a=1ll*a*b%Mod;}
	inline void Mmod(int &a) {a=(a%Mod+Mod)%Mod;}
	inline int  Cadd(int a,int b)  {return a+b>=Mod?a+b-Mod:a+b;}
	inline int  Cdel(int a,int b)  {return a-b<0?a-b+Mod:a-b;}
	inline int  Cmul(int a,int b)  {return a*b%Mod;}
	inline int  Cmod(int a)  {return (a%Mod+Mod)%Mod;}
	inline int  gcd(int a,int b)   {return b?gcd(b,a%b):a;}
	inline int  qpow(int a,int b)  {int res=1; while(b) {if(b&1) Mmul(res,a); Mmul(a,a); b>>=1;} return res;}
	inline int  qmul(int a,int b)  {int res=0; while(b) {if(b&1) Madd(res,a); Madd(a,a); b>>=1;} return res;}
	template<typename T> inline T pow(T x)    {return x*x;}
}
using namespace MyTool;

inline void file()
{
	freopen(".in","r",stdin);
	freopen(".out","w",stdout);
	return;
}

bool Mbe;

namespace LgxTpre
{
	static const int MAX=500000;
	static const int inf=2147483647;
	static const int INF=4557430888798830399;
	static const int mod=998244353;
	static const int bas=131;
	
	int n,val[MAX],fa[MAX];
	vector<int> G[MAX];
	
	int f[MAX][3][2];
	void dfs(int now)
	{
		int zsum1=0,zsum2=0,zmix=INF;
		int fsum1=0,fsum2=0,fmix=INF; 
		for(auto to:G[now]) 
		{
			dfs(to);
			zsum1+=max({f[to][0][0]+val[to],f[to][1][0],f[to][2][0]});
			zsum2+=max({f[to][0][0]+val[to],f[to][1][0]});
			cmin(zmix,max({f[to][0][0]+val[to],f[to][1][0],f[to][2][0]})-max({f[to][1][1],f[to][2][1]}));
			fsum1+=max({f[to][0][0]-val[to],f[to][1][0],f[to][2][0]});
			fsum2+=max({f[to][0][0]-val[to],f[to][1][0]});
			cmin(fmix,max({f[to][0][0]-val[to],f[to][1][0],f[to][2][0]})-max({f[to][1][1],f[to][2][1]}));
		}
		f[now][0][0]=max(zsum1,fsum1);
		f[now][1][0]=max(zsum1-zmix,fsum1-fmix);
		f[now][1][1]=max(zsum1-zmix+val[fa[now]],fsum1-fmix-val[fa[now]]);
		f[now][2][0]=max(zsum2-val[now],fsum2+val[now]);
		f[now][2][1]=max(zsum2-val[now]+val[fa[now]],fsum2+val[now]-val[fa[now]]);
		return;
	}
	
	inline void lmy_forever()
	{
		n=read();
		for(int i=1;i<=n;++i) val[i]=read();
		for(int i=2;i<=n;++i) fa[i]=read(),G[fa[i]].pb(i);
		dfs(1);
		write(max({f[1][1][0],f[1][2][0]}),'\n');
		return;
	}
}

bool Med;

signed main()
{
//	file();
	fprintf(stderr,"%.3lf MB\n",(&Med-&Mbe)/1048576.0);
	LgxTpre::lmy_forever();  
	cerr<<1e3*clock()/CLOCKS_PER_SEC<<" ms\n";
	return (0-0);
}
  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值