三校联考20181024T2 点亮light

题意:
题面
子任务

题解:
首先bb一下,本蒟蒻即将被第4个学校的dalao们吊打。
wor,考场上做这道题时接连看错两次题意,简直差点自闭,还好暴力分给得足。
然后吐槽一下题解的玄学程度…如果脸黑的话随机出来的数据真的还跑得过?算了就假设出数据的人没那么非,所有数据中树的深度就是期望深度 = 1 + ∑ i = 1 n 1 i = 1 + ln ⁡ n + Θ ( 1 ) =1+\displaystyle \sum_{i=1}^n{\dfrac{1}{i}}=1+\ln n+\Theta(1) =1+i=1ni1=1+lnn+Θ(1),子树大小就是期望大小 ≤ n u \le \dfrac{n}{u} un。证明过程略。其实是我自己太菜了证不来
树形dp是一个很容易想到的方向,但具体怎么实现还是有点恶心。我们很容易想到dp[i][j]为以i为根的子树上亮了j个点的答案。但是状态还要考虑到从1到i的路径上的情况,而如果强行记在状态里面的话存不下,那就一边dfs一边存状态。把所有a[u][v],b[u][v]都加到lca(u,v)上作为u与lca(u,v)之间的点权,然后求出从1到u的状态为s时u的贡献。然后在树上跑个背包即可。

代码:

#include<cstdio>
#include<cstring>
#define maxn 1005
#define D 10
#define INF 0x3f3f3f3f
int n,dp[maxn][maxn],fa[maxn][D],l[maxn][maxn],dep[maxn],siz[maxn],ans,val[maxn][maxn][2],sta[maxn];
struct node { int v; node *nxt; } edge[maxn],*head[maxn],*ncnt;
inline int max(int a,int b) { return a>b?a:b; }
void swap(int &a,int &b) { int t=a; a=b,b=t; }
void addedge(int u,int v)
{
	ncnt++;
	ncnt->v=v,ncnt->nxt=head[u];
	head[u]=ncnt;
}
void dfs1(int u,int d)
{
	siz[u]=1,dep[u]=d;
	for(node *p=head[u];p;p=p->nxt) { dfs1(p->v,d+1); siz[u]+=siz[p->v]; }
}
int lca(int u,int v)
{
	if(dep[u]<dep[v]) swap(u,v);
	int t;
	for(t=0;(1<<t)<=dep[u];t++); t--;
	for(;t>=0;t--)
		if(dep[fa[u][t]]>=dep[v]) u=fa[u][t];
	if(u==v) return u;
	for(t=0;(1<<t)<=dep[u];t++); t--;
	for(;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 d)
{
	int tmp[maxn]; 
	for(int i=0;i<=siz[u];i++) dp[u][i]=-INF;
	sta[d]=1,dp[u][1]=0;
	for(int i=1;i<=d;i++)
		if(sta[i]) dp[u][1]+=val[u][i][1];
	if(siz[u]>1)
	{
		dp[u][0]=0;
		for(int i=1;i<=d;i++)
			if(!sta[i]) dp[u][0]+=val[u][i][0];
	}
	for(node *p=head[u];p;p=p->nxt)
	{
		int v=p->v; dfs2(v,u,d+1);
		for(int i=siz[u];i>=0;i--)
		{
			dp[u][i]+=dp[v][0];
			for(int j=1;j<=siz[v]&&j<=i;j++) dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]);
		}
	}
	memcpy(tmp,dp[u],sizeof(dp[u]));
	for(int i=0;(i<<1)<siz[u];i++) tmp[i]=-INF;
	for(int i=0;i<=siz[u];i++) dp[u][i]=-INF;
	sta[d]=0;
	if(siz[u]>1)
	{
		dp[u][1]=0;
		for(int i=1;i<=d;i++)
			if(sta[i]) dp[u][1]+=val[u][i][1];
	}
	dp[u][0]=0;
	for(int i=1;i<=d;i++)
		if(!sta[i]) dp[u][0]+=val[u][i][0];
	for(node *p=head[u];p;p=p->nxt)
	{
		int v=p->v; dfs2(v,u,d+1);
		for(int i=siz[u];i>=0;i--)
		{
			dp[u][i]+=dp[v][0];
			for(int j=1;j<=siz[v]&&j<=i;j++) dp[u][i]=max(dp[u][i],dp[u][i-j]+dp[v][j]);
		}
	}
	for(int i=(siz[u]+1)>>1;i<=siz[u];i++) dp[u][i]=-INF;
	for(int i=0;i<=siz[u];i++) dp[u][i]=max(dp[u][i],tmp[i]);
}
void read(int &x)
{
	char ch=getchar(); int f=1; x=0;
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') f=-1,ch=getchar();
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+f*(ch-'0');
}
int main()
{
	scanf("%d",&n); ncnt=&edge[0];
	for(int i=2;i<=n;i++) { read(fa[i][0]); addedge(fa[i][0],i); }
	dfs1(1,1);
	for(int j=1;j<D;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][dep[l[i][j]]][0]+=a;val[i][dep[l[i][j]]][1]+=b;
			}
	dfs2(1,0,1);
	for(int i=0;i<=n;i++) ans=max(ans,dp[1][i]);
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值