CF专项训练
虽然 CF 在洛谷上已死,但是有好题的话我肯定是要记录一下的啊!!!
Maximum Subsequence
很厉害的一道题,提醒我们看到数据范围非常小无非就是高复杂度的 dp 和搜索,但当dp不利于操作时就考虑搜索,搜索不仅可以考虑记忆化剪枝,还可以考虑双端搜索减少复杂度。
上面就是这题的大体思路,因为我们要用取模操作所以无非用 dp 做,考虑搜索,将数组分为前后两段,分别进行搜索,此时我们想到再对两段进行组合配对不就可以了吗,但是这样的话复杂度完全没降下去。
这时我们考虑贪心,我们要想到有这么一个性质,对于其中一个数组中的数 \(q_i\) 他在 \(p\) 中进行组合时在不超过 \(m\) 的情况下时可以可以找到一个最大的不超过 \(m\) 的 \(p_i\),这比选 所有小于 \(p_i\) 都更优,当我们选择组合超过 \(m\) 时也选择,那用最大 \(q_i +\) 最大不超过也是更优的,所以我们可以排序后双指针或者二分查找即可。
总结:读题后看数据范围推测算法复杂度,搜索多考虑性质剪枝。
Yet Another Minimization Problem
不是很寻常的公式题,这种题只要操作不是很复杂都可以优先拆开。
拆到这我们现放一放,先再看一个式子,我们对他展开。
到这我们发现这两个式子是几乎是等价的,只有前面的平方和不同,现在我们的目的就是为了求平方和与和的平方最小,因为前者是个常量不管他,我们需要去求和。
我们如果确定了 \(a\) 序列的总和,那 \(b\) 的总和我们就知道了,看到 \(n\) 很小,和的总值域也很小,所以我们直接背包,设 \(f[i][sum]\) 表示选了前 \(i\) 个数总和为 \(sum\) 的这个 \(sum\) 值时我选了几个数得到的(应该这么形容,大概理解下),然后我们可以对 \(sum\) 从 \(f[i-1][sum-a[i]]+1\) 转移也可以从 \(f[i-1][sum-b[i]]+1\) 转移过来,最后哪个值是由选择n个数得到的对那个 \(sum\) 求值,最后的答案就是:\((n-2)\sum_{i=1}^n{x_i^2}+sum_a^2+(sum-sum_a)^2\)
Penchick and BBQ Buns
感觉构造题就是打表找规律及特殊性质的题,这题要求相同的数距离要是完全平方数。
n是偶数时直接两个两个放就可以1,1,2,2,3,3……
。
\(n\) 是奇数时我们必须要放奇数个,但我们又不能放 \(1\) 个,那就放 \(3\) 个,这 \(3\) 个每两个之间的距离都得是完全平方数,最边上的两个的距离是中间两个线段的和,即 \(z^2=x^2+y^2\),震惊!!!这不是勾股定理吗,所以说我们构造这个就可以满足了,最小的勾股数是3,4,5 那对应得我们得在 \(x_1,x_{10},x_{26}\) 位置放下 \(3\) 个一样的数,其他位置 \(2\) 个 \(2\) 个放,那就知道了,\(n\) 为奇数且 \(n< 26\),无解,\(n\ge 27\) 如上构造。
Alice's Adventures in Cutting Cake
特性有连续分蛋糕,答案是个区间最大值,我们预处理出来前缀 \(1-i\) 的划分可分给几个人和后缀 \(i-n\) 分人。
然后我们枚举起点,二分查找到可满足的最右边的点,中间的全都是我们可以吃到的,区间求和用前缀和即可。
Robert Hood and Mrs Hood
考虑用差分将区间贡献转化为点贡献,对于一个工作区间 \([l,r]\),因为查询也是一个区间所以将位置 \(l-d+1\) 加 \(1\),\(r+1\) 减 \(1\)。
我们再求前缀和就可以得到每个位置的区间 \([i,i+d-1]\) 不同工作数,求最大值最小值的下标即可。
#include <bits/stdc++.h> #define int long long #define re register const int N=1e5+10; using namespace std; int n,d,m; int a[N]; void solve(){ cin>>n>>d>>m; for(int i=1;i<=m;i++){ int l,r; cin>>l>>r; a[max(1ll,l-d+1)]++; a[r+1]--; } for(int i=1;i<=n;i++){ a[i]+=a[i-1]; } int mx=0,mxid=0,mi=1e9,miid=0; for(int i=1;i<=n-d+1;i++){ if(a[i]>mx){ mx=a[i]; mxid=i; } if(a[i]<mi){ mi=a[i]; miid=i; } } cout<<mxid<<" "<<miid<<"\n"; for(int i=1;i<=n+1;i++){//要多清一个位置 a[i]=0; } } signed main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin>>t; while(t--){ solve(); } return 0; }
明显树形 dp 题,设状态 \(f_{i,0/1}\) 为将当前节点不染/染成白色的最大权值和,那对于不染这个节点可以直接转移 \(f_{i,0}=\sum_{j\in son_i} max{\{ f_{j,0},f_{j,1} \}}\)。
否则染这个节点的话,儿子不染色则可以直接转移,儿子染色的需要减去 \(2c\)(儿子节点减 \(c\),当前节点减 \(c\),总权值和减 \(2c\)),转移方程有 \(f_{i,1}=\sum_{j\in son_i} max{\{ f_{j,0},f_{j,1}-2c \}}\)
#include <bits/stdc++.h> #define int long long #define ls p<<1 #define rs p<<1|1 #define re register const int N=2e5+10; const int mod=998244353; using namespace std; int n,c; int f[N][3]; vector<int> v[N]; void dfs(int x,int fa){ for(int y:v[x]){ if(y==fa){ continue; } dfs(y,x); f[x][0]+=max(f[y][0],f[y][1]); f[x][1]+=max(f[y][0],f[y][1]-2*c); } } void solve(){ cin>>n>>c; for(int i=1;i<=n;i++){ cin>>f[i][1]; } for(int i=1;i<=n-1;i++){ int u,vv; cin>>u>>vv; v[u].push_back(vv); v[vv].push_back(u); } dfs(1,0); cout<<max(f[1][1],f[1][0])<<"\n"; for(int i=1;i<=n;i++){ f[i][0]=f[i][1]=0; v[i].clear(); } } signed main(){ ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin>>t; while(t--){ solve(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」