CF1830D Mex Tree
梳理思路。
尽可能讲的清晰些。
首先考虑贪心交替染色,但是不正确的,反例就是一个端点左右各一车子点,这样以这个端点为中转站,一边全部填 ,另一边全部填 更优秀。
稍微观察题目就能发现,题目能取到的 仅在 中,并且一条路径上如果只有一种颜色,那就只能取到 了。
所以我们不妨从 种颜色入手,消去限制。而这样的话我们需要反过来搞,不妨考虑如果当前我们所有路径答案都是 ,然后使得取到 的和最小,就能保证答案最大。也就是理想的总贡献减去最小负贡献。
在树上,考虑只有一种颜色(单一的,对于点的限制)的影响,就可以把他转换成这个颜色所在的连通块,而对于一个连通块他对答案的贡献,在这里我们是容易计算的,假设当前连通块大小为 ,颜色是 ,那贡献就是 。
所以,我们把连通块设到状态里面,设 以 为根的子树,连通块大小为 ,连通块颜色为 ,所能产生的最小贡献。
转移的话,直接根据所设状态含义转移就好了:
这样做的话时间复杂度是 的,但是注意到这样的连通块大小不会太大,一个连通块的贡献大概算下来是 的,所以连通块最大不能超过 。
注意卡空间,用完儿子的转移后立刻释放掉,时间复杂度为 。
参考了第一篇题解的实现,这题不能通过倒序的方式实现背包,以后还是尽量使用记录答案的正序吧。
最大的启示是在正难则反的考虑以及对连通块的一个考虑。
逆天题,很新的一种技巧,自己是一步都想不到。
#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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现