CF1830D Mex Tree

Link

梳理思路。

尽可能讲的清晰些。

首先考虑贪心交替染色,但是不正确的,反例就是一个端点左右各一车子点,这样以这个端点为中转站,一边全部填 00,另一边全部填 11 更优秀。

稍微观察题目就能发现,题目能取到的 mex\operatorname{mex} 仅在 [0,2][0,2] 中,并且一条路径上如果只有一种颜色,那就只能取到 [0,1][0,1] 了。

所以我们不妨从 11 种颜色入手,消去限制。而这样的话我们需要反过来搞,不妨考虑如果当前我们所有路径答案都是 22,然后使得取到 [0,1][0,1] 的和最小,就能保证答案最大。也就是理想的总贡献减去最小负贡献。

在树上,考虑只有一种颜色(单一的,对于点的限制)的影响,就可以把他转换成这个颜色所在的连通块,而对于一个连通块他对答案的贡献,在这里我们是容易计算的,假设当前连通块大小为 xx,颜色是 yy,那贡献就是 x×(x1)×(y+1)2+x×(y+1)\frac{x\times(x-1) \times (y+1)}{2}+x\times(y+1)

所以,我们把连通块设到状态里面,设 dpi,j,0/1dp_{i,j,0/1}ii 为根的子树,连通块大小为 jj,连通块颜色为 0/10/1,所能产生的最小贡献。

转移的话,直接根据所设状态含义转移就好了:

dpi,j,0=min{dpi,j,0,dpi,j,0+dpv,k,1,dpi,jk,0+dpv,k,0(jk)×k}dp_{i,j,0}=\min\{dp_{i,j,0},dp_{i,j,0}+dp_{v,k,1},dp_{i,j-k,0}+dp_{v,k,0}-(j-k)\times k \} dpi,j,1=min{dpi,j,1,dpi,j,1+dpv,k,0,dpi,jk,1+dpv,k,12×(jk)×k}dp_{i,j,1}=\min\{dp_{i,j,1},dp_{i,j,1}+dp_{v,k,0},dp_{i,j-k,1}+dp_{v,k,1}-2\times (j-k)\times k \}

这样做的话时间复杂度是 O(n2)O(n^2) 的,但是注意到这样的连通块大小不会太大,一个连通块的贡献大概算下来是 x2x^2 的,所以连通块最大不能超过 n\sqrt n

注意卡空间,用完儿子的转移后立刻释放掉,时间复杂度为 O(nn)O(n\sqrt n)

参考了第一篇题解的实现,这题不能通过倒序的方式实现背包,以后还是尽量使用记录答案的正序吧。

最大的启示是在正难则反的考虑以及对连通块的一个考虑。

逆天题,很新的一种技巧,自己是一步都想不到。

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N =2e5+5;
const int lst=785ll;
vector<int> dp[N][2],g[N];
int n,t,siz[N],G[2][805];
void dfs(int u,int fath){
	siz[u]=1;
	dp[u][1].push_back(1e9),dp[u][0].push_back(1e9);
	dp[u][1].push_back(2),dp[u][0].push_back(1);
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		if(v==fath)	continue;
		dfs(v,u);
		int Min=1e9,Min1=1e9;
		for(int j=1;j<=min(lst,siz[u]);j++)	G[0][j]=dp[u][0][j],G[1][j]=dp[u][1][j],dp[u][0][j]=dp[u][1][j]=1e9;
		for(int j=1;j<=min(lst,siz[v]);j++)	Min=min(Min,dp[v][0][j]),Min1=min(Min1,dp[v][1][j]);
		for(int j=min(lst,siz[u])+1;j<=min(lst,siz[u]+siz[v]);j++)	dp[u][0].push_back(1e9),dp[u][1].push_back(1e9);
		for(int j=min(lst,siz[u]+siz[v]);j>=1;j--)
			for(int k=max(1ll*1,j-min(lst,siz[u]));k<=min(j-1,min(lst,siz[v]));k++){
				dp[u][0][j]=min(dp[u][0][j],G[0][j-k]+dp[v][0][k]+(j-k)*k);
				dp[u][1][j]=min(dp[u][1][j],G[1][j-k]+dp[v][1][k]+2*(j-k)*k);
			}
		for(int j=1;j<=min(lst,siz[u]);j++)
			dp[u][0][j]=min(dp[u][0][j],G[0][j]+Min1),dp[u][1][j]=min(dp[u][1][j],G[1][j]+Min);
		dp[v][0].clear(),dp[v][1].clear(), dp[v][0].shrink_to_fit(); dp[v][1].shrink_to_fit();;
		siz[u]+=siz[v];
	}
}
signed main()
{
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1,u,v;i<n;i++)	cin>>u>>v,g[u].push_back(v),g[v].push_back(u);
		dfs(1,0);
		int ans=1e9;
		for(int i=1;i<=min(siz[1],lst);i++)
			ans=min(ans,min(dp[1][0][i],dp[1][1][i]));
		cout<<n*(n-1)+2*n-ans<<endl;
		dp[1][0].clear(),dp[1][1].clear();
		for(int i=1;i<=n;i++)	g[i].clear();
		for(int i=1;i<=n;i++)	siz[i]=0;
	}
	return 0;
}
// dp_u,0/1,j , block j ,now use 0/1
posted @   June_Failure  阅读(9)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示