点亮

题意

在这里插入图片描述
在这里插入图片描述
题解

对于40%的数据, O ( 2 n n 2 ) O(2^nn^2) O(2nn2)乱整
题目中多次强调:随机得到的树
来来来直接上性质
在这里插入图片描述
众所周知?不存在的

综上,题解的意思是:随机一般是用来暴力骗分,但是这道题就是正解
下面是性质的证明

在这里插入图片描述
在这里插入图片描述
(突然发现自己在疯狂粘题解)

由于这个深度很小,所以我们可以状压
把所有的输入处理到LCA上
然后就可以dp了,d(i,j,k)表示i子树,状态为j,点亮k个
树上背包式的转移即可,注意分类讨论k与(sz+1)/2的关系
由于状态太多,所以干脆在每次操作中枚举状态
就酱吧

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1005;
const int L=13;
const int INF=1<<30;
int n,d[N][N],fa[N][L+2];
int l[N][N],deep[N];
int sz[N],ans,val[N][N][2],sta[N];
struct node{
    int u,v,nxt;
}edge[N*2];
int head[N],mcnt;
void add_edge(int u,int v){
	mcnt++;
	edge[mcnt].u=u;
	edge[mcnt].v=v;
	edge[mcnt].nxt=head[u];
	head[u]=mcnt;
}
void dfs1(int u,int d){
	sz[u]=1,deep[u]=d;
	for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].v;
        dfs1(v,d+1);
        sz[u]+=sz[v];
    }
}
int LCA(int u,int v){
	if(deep[u]<deep[v])
        swap(u,v);
	for(int t=L;t>=0;t--)
		if(deep[fa[u][t]]>=deep[v])
            u=fa[u][t];
	if(u==v)
        return u;
	for(int t=L;t>=0;t--)
		if(fa[u][t]!=fa[v][t])
            u=fa[u][t],v=fa[v][t];
	return fa[u][0];
}
void dfs2(int u,int fa,int dep){
	int tmp[N];
	for(int i=0;i<=sz[u];i++)
        d[u][i]=-INF;
	sta[dep]=1,d[u][1]=0;
	for(int i=1;i<=dep;i++)
		if(sta[i])
            d[u][1]+=val[u][i][1];
	if(sz[u]>1){
		d[u][0]=0;
		for(int i=1;i<=dep;i++)
			if(!sta[i])
                d[u][0]+=val[u][i][0];
	}
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
        dfs2(v,u,dep+1);
		for(int i=sz[u];i>=0;i--){
			d[u][i]+=d[v][0];
			for(int j=1;j<=sz[v]&&j<=i;j++)
                d[u][i]=max(d[u][i],d[u][i-j]+d[v][j]);
		}
	}
	memcpy(tmp,d[u],sizeof(d[u]));
	for(int i=0;(i<<1)<sz[u];i++)
        tmp[i]=-INF;
	for(int i=0;i<=sz[u];i++)
        d[u][i]=-INF;
	sta[dep]=0;
	if(sz[u]>1){
		d[u][1]=0;
		for(int i=1;i<=dep;i++)
			if(sta[i])
                d[u][1]+=val[u][i][1];
	}
	d[u][0]=0;
	for(int i=1;i<=dep;i++)
		if(!sta[i])
            d[u][0]+=val[u][i][0];
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].v;
        dfs2(v,u,dep+1);
		for(int i=sz[u];i>=0;i--){
			d[u][i]+=d[v][0];
			for(int j=1;j<=sz[v]&&j<=i;j++)
                d[u][i]=max(d[u][i],d[u][i-j]+d[v][j]);
		}
	}
	for(int i=(sz[u]+1)>>1;i<=sz[u];i++)
        d[u][i]=-INF;
	for(int i=0;i<=sz[u];i++)
        d[u][i]=max(d[u][i],tmp[i]);
}
void Read(int &x){
	char c=getchar();
	int f=1; x=0;
	while((c<'0'||c>'9')&&c!='-')
        c=getchar();
	if(c=='-')
        f=-1,c=getchar();
	while(c>='0'&&c<='9')
        x=x*10+c-'0',c=getchar();
    x*=f;
}
int main()
{
	freopen("light.in","r",stdin);
	freopen("light.out","w",stdout);
	Read(n);
	for(int i=2;i<=n;i++){
        Read(fa[i][0]);
        add_edge(fa[i][0],i);
    }
	dfs1(1,1);
	for(int j=1;j<=L;j++)
		for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
            l[i][j]=l[j][i]=LCA(i,j);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i!=j){
				int a,b;
				Read(a);
				Read(b);
				val[i][deep[l[i][j]]][0]+=a;
				val[i][deep[l[i][j]]][1]+=b;
			}
	dfs2(1,0,1);
	for(int i=0;i<=n;i++)
        ans=max(ans,d[1][i]);
	printf("%d\n",ans);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值