[考试记录] 2024.7.5
T1 酸碱度中和
题目描述
小明有 瓶生理盐水,由于浓度不太一样, 以及混进来了一些奇怪的东西,第𝑖i瓶生理盐水的酸碱度是 。
小明觉得 个瓶子太多了,于是他决定把这 瓶盐水重新灌装进 个瓶子中。
把若干瓶盐水混到一起的前提条件是:每一瓶盐水的酸碱度是一样的。
这显然太困难了,所以小明准备去哆啦A梦的杂货铺购买道具“酸碱度修改器”。
“酸碱度修改器”有一个属性值 ,当你使用它在某一瓶盐水上的时候,可以把这瓶盐水的酸碱度增加/减少最多 。比如你有一个属性为 的“酸碱度修改器”,那么你可以把原来酸碱度为 的生理盐水的酸碱度修改为 ,,,,,, 中的任何一个值。
“酸碱度修改器”可以重复使用。但是,对于每一瓶生理盐水来说只能使用一次。
属性值 越大的“酸碱度修改器”越贵,因此,小明决定购买 尽量小的,请帮助小明算一算,他最少要买属性为多少的“酸碱度修改器”。
输入格式
第一行输入 ,。
接下来一行输入 个正整数表示 。
输出格式
一个数字表示答案。
输入数据 1
4 2 1 3 5 7
输出数据 1
1
输入数据 2
4 1 1 3 5 7
输出数据 2
3
数据范围
对于30%的数据:。
对于50%的数据:。
对于另外20%的数据:。
对于100%的数据:。 全部数据
解析
本场考试最大的失误,看到 没往二分那里想,先考虑的 DP,然后发现不可做,于是打暴力拿部分分。
正解 二分答案。可以先把序列从小到大排序,可以发现修改器的最大值即为 ,其中 代表极差。由于随着修改器数值的减小,整个序列分成的段数就越多,满足单调性。那么可以二分这个修改器的属性,看能否把序列分成 段即可。
#include<bits/stdc++.h> using namespace std; constexpr int N = 1e5 + 1; int n, k, a[N]; inline bool check(int t){ int cnt = 1, lst = 1, i = 1; while(i <= n) if(a[i] - a[lst] > t) ++cnt, lst = i; else ++i; return cnt<=k; } int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>k; if(k >= n) return cout<<'0', 0; for(int i=1; i<=n; ++i) cin>>a[i]; sort(a+1, a+1+n); int l = 0, r = a[n]-a[1]+1; while(l < r){ int mid = (l + r) >> 1; if(check(mid)) r = mid; else l = mid+1; } return cout<<(l+1>>1), 0; }
T2 聪明的小明
题目描述
小明开了个酒厂,他的酒厂里面会出产 种酒。
有一天,市长要来他的酒厂视察,他可太高兴了。
为了应对这次视察,他决定在一个陈列长廊上摆放 瓶酒。市长走过这个长廊的时候就会看到每一瓶酒。
通过市长秘书处的打听,小明得到了一个重要消息:市长将会在考察完毕后,从长廊里面连续的取走 瓶酒作为纪念。
为了让市长带走的酒里,一定包含酒厂中产出的每一种酒,小明决定仔细研究这 瓶酒具体来摆放哪些酒。
请帮助小明算一算,他有多少种摆放酒的方案吧!
输入格式
第一行三个正整数 , ,,如题所述。
输出格式
输出答案 。
样例 #1
样例输入 #1
4 2 3
样例输出 #1
10
提示
【样例 1 解释】
一共1010种方案:
[1121]
,[1122]
,[1211]
,[1212]
,[1221]
,[2212]
,[2211]
,[2122]
,[2121]
,[2112]
样例输入 #2
10 4 6
样例输出 #2
81552
样例输入 #3
100000 7 10
样例输出 #3
77680521
数据范围
对于25%的数据:。
对于另5%的数据:。
对于另20%的数据:。
对于另20%的数据:。
对于100%的数据:。
解析
看到时一眼组合题,然后发现不可做。于是又打暴力大部分分。
部分分
其实当 时,总方案数为 。
当 时,可用容斥原理求解,方案数为 (来自 dyk 大佬的推理,我不会)。
正解
对,没错,状压 DP。考虑其中一段 序列,假设 , ,于是有 、 等等。可以发现 和 和 的贡献是一样的,于是就可以只记录每种酒最后出现的位置 ,进一步 和 是一样的,于是可简化为 01串 。看一眼范围 所以总共有 1<<10
种状态,状态合法的条件是状态中 的数量等于 ,并且最高位一定为 。然后计算每种状态对应的 dp[m][s]
。接下来就是递推操作。可以从初始状态 dp[m][s]
开始递推,对于每一次转移,考虑这个 01 串中哪一个 被替换到前面即可。例如 可以从 、 和 转移而来。复杂度 。
最后滚动数组优化。
#include<bits/stdc++.h> using namespace std; #define lb(x) (x&-x) constexpr int M = 998244353, N = 1e5 + 1; int n, k, m, dp[2][(1<<10)+1], ans, x=1, y; vector<int> base, G[(1<<10)+1]; inline int get(int t){ int ans = 1; for(int i=0, num=k; i<m; ++i){ ans = (__int128)ans * num % M; if(t&(1<<i)) --num; } return ans; } int main(){ ios::sync_with_stdio(0), cout.tie(0), cin.tie(0); cin>>n>>k>>m; for(int i=1<<(m-1); i<(1<<m); ++i){ if(__builtin_popcount(i) == k){ base.push_back(i); dp[x][i] = get(i); } } for(int a : base) for(int b : base){ for(int i=1, t=b; i<=k; ++i, t-=lb(t)){ if((b-lb(t)+(1<<m))>>1 == a) { G[a].push_back(b); break; } } } for(int i=m+1; i<=n; ++i){ x ^= 1, y ^= 1; for(int a : base) { dp[x][a] = 0; for(int b : G[a]) dp[x][a] = (__int128)(dp[x][a] + dp[y][b]) % M; if(i == n) ans = (__int128)(ans + dp[x][a]) % M; } } return cout<<ans, 0; }
T3 线段树
解析
#include<bits/stdc++.h> using namespace std; constexpr int N = 1e5 + 1; int n, q, dp[501][501], m[510][510]; int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>q; for(int i=1, l, r; i<=q; ++i) cin>>l>>r, ++m[l][1], --m[l][r+1]; for(int j=1; j<=n; ++j) for(int i=1; i<=n; ++i) m[i][j] += m[i-1][j] + m[i][j-1] - m[i-1][j-1]; for(int i=1; i<=n; ++i) dp[i][i] = m[i][i]; for(int i=2; i<=n; ++i) for(int j=i-1; j>=1; --j){ dp[j][i] = INT_MAX; for(int k=i-1; k>=j; --k) dp[j][i] = min(dp[j][i], dp[j][k] + dp[k+1][i] - m[j][i]); } return cout<<dp[1][n], 0; }
T4 公路 (CSP-J 2023)
题目描述
小苞准备开着车沿着公路自驾。
公路上一共有 个站点,编号为从 到 。其中站点 与站点 的距离为 公里。
公路上每个站点都可以加油,编号为 的站点一升油的价格为 元,且每个站点只出售整数升的油。
小苞想从站点 开车到站点 ,一开始小苞在站点 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 公里。问小苞从站点 开到站点 ,至少要花多少钱加油?
输入格式
输入的第一行包含两个正整数 和 ,分别表示公路上站点的数量和车每升油可以前进的距离。
输入的第二行包含 个正整数 ,分别表示站点间的距离。
输入的第三行包含 个正整数 ,分别表示在不同站点加油的价格。
输出格式
输出一行,仅包含一个正整数,表示从站点 开到站点 ,小苞至少要花多少钱加油。
样例 #1
样例输入 #1
5 4 10 10 10 10 9 8 9 6 5
样例输出 #1
79
提示
【样例 1 解释】
最优方案下:小苞在站点 买了 升油,在站点 购买了 升油,在站点 购买了 升油。
【样例 2】
见选手目录下的 road/road2.in 与 road/road2.ans。
【数据范围】
对于所有测试数据保证:,,,。
测试点 | 特殊性质 | |
---|---|---|
无 | ||
无 | ||
A | ||
B | ||
无 |
- 特殊性质 A:站点 的油价最低。
- 特殊性质 B:对于所有 , 为 的倍数。
解析
没想到我竟然推了个 DP,还是个看上去能斜率优化的,但是斜率递增,x 坐标不递增,而且没有考虑到加满后走了一段还剩下的情况。
正解
贪心。找到下一个比当前节点价格低的节点:
- 油箱剩下的油能走到,
continue
。 - 加满油后一定能走到,只加走到那里需要的油。
- 加满油走不到,直接加满即可。
#include<bits/stdc++.h> using namespace std; #define int long long constexpr int N = 1e5 + 1; int n, c, sum[N], v[N], ans, cn; signed main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>c; for(int i=1, s; i<=n; ++i) cin>>s, sum[i] = sum[i-1] + s; for(int i=0; i<n; ++i) cin>>v[i]; for(int i=0; i<=n; ++i){ if(i) cn -= sum[i] - sum[i-1]; if(i == n) break; int nxt = i+1; while(v[nxt] > v[i]) ++nxt; if(sum[nxt] - sum[i] <= cn) continue; else if(sum[nxt] - sum[i] <= c) ans += (sum[nxt]-sum[i]-cn)*v[i], cn = sum[nxt]-sum[i]; else ans += (c-cn)*v[i], cn = c; } return cout<<ans, 0; }
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18286707
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步