20181023模拟赛T2 点亮【DP】

7 篇文章 0 订阅
2 篇文章 0 订阅

题意:

一颗有 n n n个节点的有根树( n &lt; = 1000 n&lt;=1000 n<=1000),根为1,保证 f a [ i ] &lt; = i fa[i]&lt;=i fa[i]<=i且数据随机生成。
现在你可以点亮某些节点,定义每个有序点对 ( i , j ) (i,j) (i,j)的贡献如下:
i i i被点亮且子树 l c a ( i , j ) lca(i,j) lca(i,j)点亮的点数大于等于没有点亮的点数, a n s + = b [ i ] [ j ] ans+=b[i][j] ans+=b[i][j];
i i i没有点亮且子树 l c a ( i , j ) lca(i,j) lca(i,j)没有点亮的点数大于点亮的点数, a n s + = a [ i ] [ j ] ans+=a[i][j] ans+=a[i][j];
否则没有贡献。

分析:

因为在大概率下,树的深度小于20,所以单独考虑每个节点的贡献,壮压它的所有祖先点亮的点数是否大于等于没有点亮的点数,这样祖先的状态就确定了(先暂时不考虑祖先的状态的合法性,因为不合法的情况会在祖先哪里被删掉,不会算入最终答案)。在祖先情况确定后,再考虑当前节点:
定义 d p [ u ] [ i ] dp[u][i] dp[u][i]:以 u u u为根的子树中,有 i i i个点被点亮的最大贡献。
转移是常见树形DP套路,遍历 u u u的所有儿子节点 v v v,在 v v v的祖先的状态继承了 u u u的祖先的状态的情况下: d p [ u ] [ i ] = m a x ( d p [ u ] [ i ] , d p [ v ] [ k ] + d p [ u ] [ i − k ] ) dp[u][i]=max(dp[u][i],dp[v][k]+dp[u][i-k]) dp[u][i]=max(dp[u][i],dp[v][k]+dp[u][ik])
i i i从大到小枚举就不会受 v v v的影响。
在树形DP前要 O ( n 2 ) O(n^2) O(n2)预处理 l c a ( i , j ) lca(i,j) lca(i,j),对于每个点 u u u可能会有的贡献要提前存入 v a l [ u ] [ d e p ] [ 0 / 1 ] val[u][dep][0/1] val[u][dep][0/1]中(方便统计), v a l [ u ] [ d e p ] [ 0 / 1 ] val[u][dep][0/1] val[u][dep][0/1]表示 u u u被点亮 ( 1 , + b [ u ] [ v ] ) (1,+b[u][v]) (1,+b[u][v])或没有点亮 ( 0 , + a [ u ] [ v ] ) , l c a ( u , v ) (0,+a[u][v]),lca(u,v) (0,+a[u][v]),lca(u,v)的深度为 d e p dep dep的贡献。
初始状态:
若这个点被点亮:
d p [ u ] [ 1 ] = ∑ i = 1 d e p [ u ] ( s t a [ i ] = = 1 ) ∗ v a l [ u ] [ i ] [ 1 ] dp[u][1]=\sum_{i=1}^{dep[u]}(sta[i]==1)*val[u][i][1] dp[u][1]=i=1dep[u](sta[i]==1)val[u][i][1]
若这个点没有点亮:
d p [ u ] [ 0 ] = ∑ i = 1 d e p [ u ] ( s t a [ i ] = = 0 ) ∗ v a l [ u ] [ i ] [ 0 ] dp[u][0]=\sum_{i=1}^{dep[u]}(sta[i]==0)*val[u][i][0] dp[u][0]=i=1dep[u](sta[i]==0)val[u][i][0]
退出时 d p [ u ] [ i ] dp[u][i] dp[u][i]取点亮 u u u或不点亮中的最大值。
刚才说要壮压祖先的状态,其实直接DFS就好,因为如果开壮压的数组,内存会炸。
别的就详见代码吧。
时间复杂度:O(玄学) O ( ∑ u = 1 n 2 d e p [ u ] ∗ s i z e [ u ] 2 ) O(\sum_{u=1}^n2^{dep[u]}*size[u]^2) O(u=1n2dep[u]size[u]2)(难以置信如此复杂度竟没有TLE,数据出得真好,呵呵)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 1025
#define INF 100000000
void Max(int &a,int b)
{
	if(b>a) a=b;
}
int n,fa[MAXN],lca[MAXN][MAXN],dfn,st[MAXN],l[MAXN],r[MAXN],dep[MAXN];
int Adj[MAXN],V[MAXN],nxt[MAXN],c;
int	val[MAXN][20][2];
void AddEdge(int u,int v)
{
	c++;V[c]=v,nxt[c]=Adj[u];Adj[u]=c;
}
void dfs(int u)
{//求dep,siz,lca
	l[u]=++dfn;r[u]=dfn;
	st[dfn]=u;dep[u]=dep[fa[u]]+1;
	for(int i=Adj[u];i;i=nxt[i])
	{
		int v=V[i];
		dfs(v);
		for(int j=l[v];j<=r[v];j++)
			for(int k=l[u];k<=r[u];k++)
				lca[st[j]][st[k]]=lca[st[k]][st[j]]=u;
		r[u]=r[v];
	}
}
int sta[MAXN],f[MAXN][MAXN];
//sta:存祖先状态
void DFS(int u,int d)
{
	int tmp[MAXN];
	int sizu=r[u]-l[u]+1;
	for(int i=0;i<=sizu;i++) f[u][i]=-INF;
	sta[d]=1;
	f[u][1]=0;
	for(int i=1;i<=d;i++) if(sta[i]) f[u][1]+=val[u][i][1];
	if(sizu>1)
	{
		f[u][0]=0;
		for(int i=1;i<=d;i++) if(!sta[i]) f[u][0]+=val[u][i][0];
	}
	for(int i=Adj[u];i;i=nxt[i])
	{
		int v=V[i];
		DFS(v,d+1);
		for(int j=sizu,sizv=r[v]-l[v]+1;j>=0;j--)
		{
			f[u][j]+=f[v][0];
			for(int k=1;k<=sizv&&k<=j;k++)
				Max(f[u][j],f[u][j-k]+f[v][k]);
		}
	}
	memcpy(tmp,f[u],sizeof f[u]);
	for(int i=0;(i<<1)<sizu;i++) tmp[i]=-INF;//将不合法的状态清除掉
	for(int i=0;i<=sizu;i++) f[u][i]=-INF;
	sta[d]=0;
	if(sizu>1)
	{
		f[u][1]=0;
		for(int i=1;i<=d;i++) if(sta[i]) f[u][1]+=val[u][i][1];
	}
	f[u][0]=0;
	for(int i=1;i<=d;i++) if(!sta[i]) f[u][0]+=val[u][i][0];
	for(int i=Adj[u];i;i=nxt[i])
	{
		int v=V[i];
		DFS(v,d+1);
		for(int j=sizu,sizv=r[v]-l[v]+1;j>=0;j--)
		{
			f[u][j]+=f[v][0];
			for(int k=1;k<=sizv&&k<=j;k++)
				Max(f[u][j],f[u][j-k]+f[v][k]);
		}
	}
	for(int i=sizu+1>>1;i<=sizu;i++) f[u][i]=-INF;//将不合法状态清除
	for(int i=0;i<=sizu;i++) Max(f[u][i],tmp[i]);
}
int main()
{
	freopen("light.in","r",stdin);
	freopen("light.out","w",stdout);
	scanf("%d",&n);
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&fa[i]);
		AddEdge(fa[i],i);
	}
	dfs(1);
	for(int i=1,a,b;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(j!=i)
			{
				scanf("%d%d",&a,&b);
				val[i][dep[lca[i][j]]][0]+=a;
				val[i][dep[lca[i][j]]][1]+=b;
			}
	DFS(1,1);
	int ans=-INF;
	for(int i=0;i<=n;i++) Max(ans,f[1][i]);
	printf("%d\n",ans);
}

我个人觉得这道题是没有正解的,因为题解用到的壮压DP有一个要求:某个节点的祖先的个数必须小于等于20(或者不超过20太多,从数据来看,没有哪个数据的树的深度大于了20),否则会TLE。尽管数据保证了随机生成,但有保证是严格的log吗?随机一棵树在平摊情况下深度是log,但不是一定小于等于log,就算大于log是小概率事件,但它是不可能事件(或概率小到忽略不计)吗?既然它不是不可能事件,那么凭什么这种做法能称之为正解。我想问出题人,你在生成数据的时候,就没有生成出你的正解过不了的数据吗?或许是没有的,毕竟或许只生成5组数据。题目没有任何一句话保证树的深度小于20,如果你保证了数据没有树的深度大于20,为什么不在题面中明确写出来,让做题的人来猜测数据是不是都很水有意思吗?我是有些抗拒写这份题解的,因为它本就没有正解!
但总体来说,dp还算比较有趣,如果没有如此让人望而生畏的玄学复杂度就好了。
考试的时候想到了枚举祖先状态,但不敢写啊,呵呵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值