CF2138C-Maple and Tree Beauty

CF2138C1-Maple and Tree Beauty (Easy Version)

CF2138C2-Maple and Tree Beauty (Hard Version)

题目大意

给你一棵根为 1 1 1 的树,树上每个结点都可以被设置为 0 0 0 或 1 1 1 ,现在给你 k k k 个 0 0 0 。对于每个叶子节点有一个对应的字符串,字符串由根到叶子结点的简单路径的 01 01 01 值拼成, S s o n = S f a + w s o n S_{son}=S_{fa}+w_{son} Sson=Sfa+wson 。

求所有叶子字符串的最长公共子序列长度。

题解

这道题由 e z v e r s i o n ezversion ezversion 和 h d v e r s i o n hdversion hdversion 组成。

考虑极端情况,字符串的最长公共子序列长度最大为深度最小的叶节点的深度 , m i n l e a f d e p minleafdep minleafdep。

对于该深度上的每一行,我们尽可能的让每一行的数都一样。而此深度下的数根本无所谓。我们贪心的从上到下尽可能的让每一行数字都一样,你会发现只有到 m i n l e a f d e p minleafdep minleafdep 层时才会无法满足。所以答案一定在 minleafdep,minleafdep-1 之中。

我们统计 m i n l e a f d e p minleafdep minleafdep 之上每一层的节点数,那么问题就变成了你可以选一些层染黑,问能否恰好选出 k k k 个节点。

很显然可以背包,时间复杂度 O ( n 2 ) O(n^2) O(n2) 。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

const int N=2e5+5;
int n,k,dep[N],son[N];
vector<int> e[N];
int cnt[N];
bitset<N> dp;

void solve(){
	cin>>n>>k;
	fill(son+1,son+n+1,1);
	fill(cnt+1,cnt+n+1,0);
	dp.reset();
	fill(dep+1,dep+n+1,0);
	dep[1]=1;cnt[1]=1;
	for(int i=2;i<=n;i++){
		int f;cin>>f;
		dep[i]=dep[f]+1;
		son[f]=0;
		cnt[dep[i]]++;
	}
	int md=1e9;
	for(int i=1;i<=n;i++) if(son[i]) md=min(md,dep[i]);
	dp[0]=1;
	int sum=0;
	for(int i=1;i<=md;i++){
		sum+=cnt[i];
		for(int j=sum-cnt[i];j>=0;j--) dp[j+cnt[i]]=dp[j+cnt[i]]|dp[j];
	}
	if(sum<=max(k,n-k)){
		cout<<md<<endl;
		return;
	}
	for(int i=0;i<=sum;i++){
		if(dp[i]&&i<=k&&sum-i<=n-k){
			cout<<md<<endl;
			return;
		}
	}
	cout<<md-1<<endl;
}

signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	int T;cin>>T;while(T--) solve();
	return 0;
}

对于 e z v e r s i o n ezversion ezversion 上面的思路足够了。但是对于 h d v e r s i o n hdversion hdversion 我们还需要进行优化。

注意到上文中的代码有一句 for(int j=sum-cnt[i];j>=0;j--) dp[j+cnt[i]]=dp[j+cnt[i]]|dp[j];(即背包的转移),且这是整个代码的复杂度瓶颈,所以考虑优化这句。

可以发现上面的代码中我们使用了 b i t s e t bitset bitset 来存储 d p dp dp 状态。理解一下,注意到这句话就是将整个 d p dp dp 数组在二进制意义下左移 c n t i cnt_i cnti 位,然后再按位或上原来的 d p dp dp 数组,等价于 dp|=(dp<<cnt[i]);

时间复杂度降为 O ( n 2 / w ) O(n²/w) O(n2/w)

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long

const int N=2e5+5;
int n,k,dep[N],son[N];
vector<int> e[N];
int cnt[N];
bitset<N> dp;

void solve(){
	cin>>n>>k;
	fill(son+1,son+n+1,1);
	fill(cnt+1,cnt+n+1,0);
	dp.reset();
	fill(dep+1,dep+n+1,0);
	dep[1]=1;cnt[1]=1;
	for(int i=2;i<=n;i++){
		int f;cin>>f;
		dep[i]=dep[f]+1;
		son[f]=0;
		cnt[dep[i]]++;
	}
	int md=1e9;
	for(int i=1;i<=n;i++) if(son[i]) md=min(md,dep[i]);
	dp[0]=1;
	int sum=0;
	for(int i=1;i<=md;i++){
		dp|=(dp<<cnt[i]);
		sum+=cnt[i];
	}
	if(sum<=max(k,n-k)){
		cout<<md<<endl;
		return;
	}
	for(int i=0;i<=sum;i++){
		if(dp[i]&&i<=k&&sum-i<=n-k){
			cout<<md<<endl;
			return;
		}
	}
	cout<<md-1<<endl;
}

signed main(){
	ios::sync_with_stdio(0),cin.tie(0);
	int T;cin>>T;while(T--) solve();
	return 0;
}
相关推荐
Tairitsu_H20 分钟前
[C++] 深入理解红黑树:封装set与map
开发语言·c++·set·map·红黑树·模拟实现
小智老师PMP1 小时前
2026深度解析|PMP第八版与NPDP核心侧重点本质区别(管理类证书怎么选)
开发语言·分布式·算法·职场和发展·产品经理
HugoStudio_SWAN1 小时前
洛谷 B4450 / B3867 / B3923 智慧购物、储蓄与做题——从程序到生活
c++·学习·程序人生·算法·生活
a187927218311 小时前
【算法】动态规划第二篇:双序列 DP 三课——继承、计步与断链
算法·leetcode·动态规划·dp·回溯·暴力·算法讲解
江畔柳前堤2 小时前
前台·中台·后台:2026年AI原生时代的架构全景图
开发语言·人工智能·算法·机器学习·架构·scala·ai-native
文心快码BaiduComate2 小时前
2026 OPC x AI Coding 趋势案例交流会圆满落幕:一个人就一支研发团队
算法
ACM-moran3 小时前
条件显化(二分单峰函数+构造数列)
算法·推公式·整数三分
会周易的程序员3 小时前
5Draft(五帝)测试报告
服务器·c++·分布式·raft·共识算法·共识·算力服务器
UIU1143 小时前
scanf与cout的误区:探究其内部的机制
c++·学习·c#·scanf
闻缺陷则喜何志丹3 小时前
【栈 运算系统】P3719 [AHOI2017初中组] rexp|普及+
c++·算法··洛谷·运算系统