2020蓝桥杯模拟赛
第九题 序列计数
题目
【问题描述】
小明想知道,满足以下条件的正整数序列的数量:
1. 第一项为 n;
2. 第二项不超过 n;
3. 从第三项开始,每一项小于前两项的差的绝对值。
请计算,对于给定的 n,有多少种满足条件的序列。
【输入格式】
输入一行包含一个整数 n。
【输出格式】
输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
【样例输入】
4
【样例输出】
7
【样例说明】
以下是满足条件的序列:
4 1
4 1 1
4 1 2
4 2
4 2 1
4 3
4 4
【评测用例规模与约定】
对于 20% 的评测用例,1 <= n <= 5;
对于 50% 的评测用例,1 <= n <= 10;
对于 80% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000。
暴力显然超时,选择记忆化
#include<bits/stdc++.h> #define mod 10000 using namespace std; int n, ans = 0, cha, vis[1005][1005], pass; void dfs(int up, int x){ ans += x - 1; ans %= mod; int k = ans; for(int i = x - 1; i > 0; i--){ cha = abs(up - i); if(vis[up][i]){ ans = (ans + vis[up][i]) % mod; k = ans; continue; } else if(cha){ dfs(i, cha); vis[up][i] = (ans - k + mod) % mod; k = ans; } } } int main(){ cin >> n; ans += n; for(int i = n; i >= 1; i--){ cha = n - i; pass = ans; if(cha){ dfs(i, cha); vis[n][i] = (ans - pass + mod) % mod; } } cout << ans; }
然而单纯加入记忆化只能保证过80%的点,所以需要进一步优化。
如果我们用f(i,j)表示前一个数是i,当前数是1到j的合法序列的个数;有f(i,j) = 1 + f(i,j-1) + f(j,abs(i-j)-1)即分为两个部分1)i作为前一个数,从1到j-1为当前数的合法序列的个数已经计算好,2)求以j为尾数,后面选择1到abs(i-j)-1的合法序列的个数。
之前是从1到j循环加,现在一步到位,时间复杂度大大降低。
#include<bits/stdc++.h> #define ri register int #define ll long long #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) using namespace std; const inline int read(){ int k = 0, f = 1; char c = getchar(); for(;!isdigit(c); c = getchar()) if(c == '-') f = -1; for(;isdigit(c); c = getchar()) k = k * 10 + c - '0'; return k * f; } const int mod = 10000; int n, vis[1005][1005]; int dfs(int pre, int cur){ if(cur <= 0) return 0; if(vis[pre][cur]) return vis[pre][cur]; return vis[pre][cur] = (1 + dfs(pre, cur - 1) + dfs(cur, abs(pre - cur) - 1)) % mod; } int main(){ cin >> n; cout << dfs(n, n) << "\n"; return 0; }
第十题 晚会节目单
题目
【问题描述】
小明要组织一台晚会,总共准备了 n 个节目。然后晚会的时间有限,他只能最终选择其中的 m 个节目。
这 n 个节目是按照小明设想的顺序给定的,顺序不能改变。
小明发现,观众对于晚会的喜欢程度与前几个节目的好看程度有非常大的关系,他希望选出的第一个节目尽可能好看,在此前提下希望第二个节目尽可能好看,依次类推。
小明给每个节目定义了一个好看值,请你帮助小明选择出 m 个节目,满足他的要求。
【输入格式】
输入的第一行包含两个整数 n, m ,表示节目的数量和要选择的数量。
第二行包含 n 个整数,依次为每个节目的好看值。
【输出格式】
输出一行包含 m 个整数,为选出的节目的好看值。
【样例输入】
5 3
3 1 2 5 4
【样例输出】
3 5 4
【样例说明】
选择了第1, 4, 5个节目。
【评测用例规模与约定】
对于 30% 的评测用例,1 <= n <= 20;
对于 60% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 100000,0 <= 节目的好看值 <= 100000。
此题关键在于“第一个节目尽可能好看”并希望“第二个节目尽可能好看”……那么我们选择的第一节目就是max(g[1]~g[n-m+1])闭区间,要选择的第二个节目是max(g[lastMax+1],g[n-m+2])即从上一个节目往下到n-m+2这个区间里面选最好看的,直到剩下的必须全部选择。
自己拿ST写的,不知道有没有问题- -
#include<bits/stdc++.h> #define ri register int #define ll long long #define fast ios::sync_with_stdio(0), cin.tie(0), cout.tie(0) using namespace std; const inline int read(){ int k = 0, f = 1; char c = getchar(); for(;!isdigit(c); c = getchar()) if(c == '-') f = -1; for(;isdigit(c); c = getchar()) k = k * 10 + c - '0'; return k * f; } const int maxn = 100005; int n, m, f[maxn][22], ans[maxn], cnt[maxn][22], now = 1; int query(int l, int r){ int k = log2(r - l + 1); if(f[l][k] >= f[r - (1 << k) + 1][k]){ now = cnt[l][k]; return f[l][k]; } else{ now = cnt[r - (1 << k) + 1][k]; return f[r - (1 << k) + 1][k]; } } int main(){ n = read(), m = read(); for(ri i = 1; i <= n; ++i){ f[i][0] = read(); cnt[i][0] = f[i][0]; } for(ri j = 1; j <= 21; ++j) for(ri i = 1; i + (1 << j) <= n + 1; ++i){ if(f[i][j - 1] >= f[i + (1 << (j - 1))][j - 1]){ f[i][j] = f[i][j - 1]; cnt[i][j] = cnt[i][j - 1]; } else{ f[i][j] = f[i + (1 << (j - 1))][j - 1]; cnt[i][j] = cnt[i + (1 << (j - 1))][j - 1]; } } for(ri i = m; i >= 1; --i) printf("%d ", query(now, n - i + 1)); printf("\n"); return 0; }


浙公网安备 33010602011771号