整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
目录
怎么这么多人AK呀 … 确实挺简单的 (●ˇ∀ˇ●) 考到了很多常用技巧,一堆智商检测题,简直好评hhh
不过怎么全是签到题呀,确实比较考察思维hhh但是比赛过的人却很少 ~ 以前前几题都是能过一万五来着,这次只有几千…
比赛链接:https://codeforces.com/contest/1485
A - Add and Divide
Problem A Add and Divide
Translation
给定两个正整数 a a a, b b b ,你可以进行两种操作:
- 使得 a = ⌊ a b ⌋ a=\lfloor\cfrac{a}{b}\rfloor a=⌊ba⌋
- 使得 b = b + 1 b = b + 1 b=b+1
请问最少多少次操作,使得 a = 0 a=0 a=0。
Word
Solution
签到题 ~
之前做过这种题目,链接:2021年洛谷一月月赛(Div1、Div2,6题)全部题解 C题,比这个难多了 ~
但是万变不离其宗,那道题同样有两个操作,加一或者 × 2 \times 2 ×2 ,最小费用修改,使得整个序列满足一个条件,需要套一个二分加DP。
中心思想还是分析性质。我们发现 + + + 操作前期比 × \times × 收益更大,后期 × \times × 操作比 + + + 操作收益更大,由于乘的话是指数级增长,所以那么数据是 1 0 9 10^9 109 ,实际的操作次数也是常数级别的,所以我们可以暴力。这道题仅要求将 a a a 除到 0 0 0,我们仅需将 b b b 加到 10 10 10 即可,然后暴力计算。
其实可以简单地证明一下,为什么到 10 10 10 一定ok,实际上一次加操作,最后的总贡献是你乘的次数,如果当你乘一次的贡献就已经超过了乘的次数,也就是说乘的贡献此时一定超过了加的贡献,所以简单算一下,我们发现 log 10 1 0 9 = 9 \log_{10}10^9=9 log10109=9, 所以只需要加到10即可 ~
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
typedef int itn;
const int N = 5007;
const ll INF = 4e18;
int n, m, t;
ll a, b;
void solve()
{
scanf("%lld%lld", &a, &b);
if(a == 0) {
puts("0");
return ;
}
ll res = INF, ans;
ll cnt = 0;
ll B = b;
if(b == 1) b ++ , cnt ++ ;
for(; b <= 10; ++ b) {
ans = 0;
cnt ++ ;
ll tmp_a = a;
while(tmp_a) {
tmp_a /= b;
ans ++ ;
}
ans += cnt;
ans -- ;
if(ans < res) res = ans;
}
if(B > 10) {
ans = 0;
while(a) {
a /= B;
ans ++ ;
}
}
res = min(res, ans);
printf("%lld\n", res);
return ;
}
int main()
{
scanf("%d", &t);
while(t -- ) {
solve();
}
}
B - Replace and Keep Sorted
Problem B Replace and Keep Sorted
Translation
对于一个正整数 k k k ,规定两个序列是 “ k k k 相似序列” 需要满足以下要求:
- 两个序列严格递增
- 两个序列有相同的长度
- 两个序列的元素的取值 ∈ [ 1 , k ] \in[1,k] ∈[1,k]
- 两个序列仅有一个位置的元素不同
给定一个正整数 k k k,一个严格递增的序列 a a a ,将会有 q q q 次询问,每次询问一个区间 l l l , r r r ,你的任务是每次输出有多少种 b b b 序列是序列 a l , a l + 1 , a l + 2 ⋯ , a r a_l,a_{l+1},a_{l+2}\cdots,a_r al,al+1,al+2⋯,ar 的 “ k k k 相似序列” 。
Word
Solution
签到题 ~
注意一个重要的性质:严格递增
因为元素的取值只能是 [ 1 , k ] [1,k] [1,k] ,并且两个序列必须只能有一个不同的元素,由于序列一定严格递增,求总的方案数,实际上对于每一个序列来说,每次只能有一个位置不同,而对于这个位置来说,手算一下发现,对于每一个区间里的数,一共有 a [ i + 1 ] − a [ i − 1 ] − 2 a[i+1]-a[i-1]-2 a[i+1]−a[i−1]−2 种选法,对于区间的边界来说,一共有 a [ i + 1 ] − a [ i − 1 ] − 2 a[i+1]-a[i-1]-2 a[i+1]−a[i−1]−2 种选法,设每一个位置 i i i 的选法就是 i i i 的权值,由加法原理可知,总方案数就是区间权值和,所以我们直接使用一个前缀和维护一下即可。
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
typedef int itn;
const int N = 2e5 + 7;
const ll INF = 4e18;
int n, m, t, q, k;
ll val[N], a[N];
ll sum[N];
int main()
{
scanf("%d%d%d", &n, &q, &k);
for(int i = 1; i <= n; ++ i) {
scanf("%d", &a[i]);
}
a[0] = 1, a[n + 1] = k;
for(int i = 1; i <= n; ++ i) {
val[i] = a[i + 1] - a[i - 1] - 2;
sum[i] = sum