安吉D8-D
- 1安吉D19 T1
- 2安吉D19 T3
- 3安吉D20 模考12总结
- 4安吉D17 T3
- 5安吉D17 T4
- 6安吉D19 模考11总结
- 7安吉D15 A
- 8安吉D16 T4
- 9安吉D17 T1
- 10安吉D17 模考10总结
- 11安吉D16 T3
- 12安吉D16 模考9总结
- 13安吉D13 模考8总结
- 14安吉D13-T2
- 15安吉D13-T3
- 16安吉D12-T3
- 17安吉D11-B
- 18安吉D11-C
- 19安吉D11-D
- 20安吉D12 模考7总结
- 21安吉D12-T2
- 22安吉D11-A
- 23安吉D10 模考6总结
- 24安吉D10-T3
- 25安吉D10-T4
- 26安吉D8-E
- 27安吉D9-T4
- 28安吉D8-A
- 29安吉D8-B
- 30安吉D8-C
- 31安吉D8-D本文
- 32安吉D9 模考5总结
- 33安吉D9-T2
- 34安吉D9-T3
- 35安吉D6-T4
- 36安吉D4-G
- 37安吉D5-T2
- 38安吉D6 模考4总结
- 39安吉D6-T1
- 40安吉D6-T3
- 41安吉-开营测试 T4
- 42安吉D5 模考3总结
- 43安吉D5-T3
- 44安吉D4-A
- 45安吉D4-B
- 46安吉D4-C
- 47安吉D4:容斥原理
- 48安吉Day4-D
- 49安吉D3 模考2总结
- 50安吉D3-T1
- 51安吉D3-T2
- 52安吉D3-T4
- 53安吉D2 模考1总结
- 54安吉D2-T2
- 55安吉D1-G
- 56安吉D1-L
- 57安吉D2-T4
原题呈现
P13037 Hidden Pancakes
题目描述
我们总共要烹饪 张煎饼.这些煎饼的半径分别为 厘米(cm)、、,……,以及 ,但烹饪顺序不一定按半径从小到大排列.烹饪完第一张煎饼后,我们直接将其放在盘子上.之后每烹饪完一张煎饼,就将其叠放在之前所有煎饼的最上方,且所有煎饼的中心对齐.这样,每张煎饼在刚被加入时都能从顶部被看到.只有当之后烹饪了比它半径更大的煎饼时,这张煎饼才会被隐藏.
例如,假设我们烹饪 4 张煎饼.首先烹饪半径为 的煎饼,此时它可见.接着烹饪半径为 的煎饼,叠放在第一张煎饼上,此时两张煎饼都可见.然后烹饪半径为 的煎饼,它会覆盖前一张煎饼(半径为 的煎饼),但不会覆盖第一张煎饼,因此此时共有 2 张煎饼可见.最后,烹饪半径为 的煎饼,它会覆盖所有其他煎饼,此时只有 1 张煎饼可见.下图展示了每张煎饼被烹饪后叠放的状态,其中完全不透明的煎饼表示可见,半透明的煎饼表示不可见.

设 表示叠放了恰好 张煎饼时可见的煎饼数量.在上面的例子中,、、、.
给定列表 ,问在所有 种可能的烹饪顺序中,有多少种顺序能恰好得到给定的 序列?由于结果可能非常大,只需输出结果对质数 (即 )取模后的值.
输入格式
输入的第一行包含测试用例数量 .每个测试用例包含两行:第一行是一个整数 ,表示烹饪的煎饼数量;第二行包含 个整数 ,分别表示叠放了 张煎饼时的可见煎饼数量.
输出格式
对于每个测试用例,输出一行 Case #x: y,其中 是测试用例编号(从 1 开始), 是满足条件的烹饪顺序数量对 取模后的结果.
输入输出样例 #1
输入 #1
341 2 2 131 1 231 1 3输出 #1
Case #1: 1Case #2: 2Case #3: 0输入输出样例 #2
输入 #2
1241 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2输出 #2
Case #1: 234141013说明/提示
样例解释
样例 #1 已在题目描述中说明,唯一的满足条件的烹饪顺序是 .
在样例 #2 中,顺序 和 均能满足给定的 序列.下图展示了这两种情况:


在样例 #3 中,叠加第二张煎饼后只有 1 张煎饼可见,因此无法通过叠加第三张煎饼使可见煎饼数量超过 2.
样例测试集 2 符合测试集 2 的限制条件,但提交的解法不会实际运行该测试集.
在测试集 2 的样例中,共有 种烹饪顺序满足给定的 序列,对 取模后的结果是 .
限制条件
- .
- 对于所有 ,.
测试集 1(可见判定)
- 时间限制:30 秒.
- .
测试集 2(隐藏判定)
- 时间限制:40 秒.
- .
直接解决问题有些难度,我们先不要想着如何确定所有煎饼的位置,先想想如何确定最大煎饼(即半径为 N 的煎饼)的位置.
显然,当最大煎饼铺在盘子上时,可见的煎饼数量一定为 1,只能看见最大煎饼.而且之后的煎饼无法覆盖这张煎饼,故后面的可见煎饼数量必然大于 1.因此,我们得知:最大的煎饼就处于 序列中最后一个 1 的位置.
得知了最大煎饼,又有什么用呢?我们知道,最大煎饼会把所有的煎饼分成上面和下面两个部分,由于有最大煎饼的阻隔,上下两个部分互不干涉,分成了两个子问题.在子问题中,我们也可以获取子区间内最后一个 1 的位置,这是子区间内的最大煎饼(当然,如果该子区间在父问题中在上面,则该区间内的每一个数字被增加了 1,即父问题中的最大煎饼是可见的,因此,此时需要查询的是子区间内最后一个 1 的位置)
一般的,对于一个子问题,它需要得到 之间煎饼排列的方案数,其会存在一个权值 .解决子问题的操作如下(设该子问题中煎饼数量为 ):
- 找出当前区间中最小的元素 ,如果 ,说明没有方案,返回 0.
- 依照这个最小的元素进行分割,可以分成上子问题和下子问题,上子问题的权值为 (因为有煎饼 的存在),下子问题的权值为 .若上子问题和下子问题的答案分别为 和 ,且上子问题的煎饼数量为 ,那么答案相当于在一共的 个煎饼中选择 个放在上子区间,为 .
- 边界情况,若该子问题中煎饼数量为 1,则表明只有 1 种情况.
一般的,根问题(最大的问题)的权值为 1.
#include <bits/stdc++.h>using namespace std;#define int long longconst int N = 1e5 + 100;const int MOD = 1e9 + 7;int n, fac[N];
struct Node { int v, id; friend bool operator<(const Node a, const Node b) { if (a.v == b.v) return a.id > b.id; return a.v < b.v; }};
Node v[N];struct ST { Node mn[18][N]; Node *a; int size; void init(Node *a, int size) { this->a = a; memset(mn, 0x3f, sizeof(mn)); for (int i = 1; i <= size; i++) { mn[0][i] = a[i]; } for (int j = 1; j <= 16; j++) { for (int i = 1; i <= size; i++) { mn[j][i] = min(mn[j - 1][i], mn[j - 1][i + (1 << (j - 1))]); } } } Node qry(int l, int r) { int len = log2(r - l + 1); return min(mn[len][l], mn[len][r - (1 << len) + 1]); }} st;
int fastPow(int a, int p) { int ans = 1, cur = a; while (p >= 1) { if (p & 1) { (ans *= cur) %= MOD; } (cur *= cur) %= MOD; p /= 2; } return ans;}
int inv(int x) { return fastPow(x, MOD - 2); }
int calFac(int x) { if (!fac[x]) { fac[x] = calFac(x - 1) * x % MOD; } return fac[x];}
int calC(int from, int select) { if (select == 0 || from == select) return 1; return calFac(from) * inv(calFac(select)) % MOD * inv(calFac(from - select)) % MOD;}
int solve_sub(int l, int r, int lower) { // cout << "DEBA " << l << "~" << r << endl; if (l > r) return 1; Node nd = st.qry(l, r); if (nd.v != lower) { return 0; } if (l == r) { return 1; } int mid = nd.id; int upper = r - mid; int ways = calC(r - l, upper); // cout << "DEBB " << l << "~" << r << ": " << mid << " " << ways << " " // << lower << " " << nd.v << endl; int left = solve_sub(l, mid - 1, lower); int right = solve_sub(mid + 1, r, lower + 1); return left * right % MOD * ways % MOD;}
void solve(int id) { cin >> n; for (int i = 1; i <= n; i++) { cin >> v[i].v; v[i].id = i; } st.init(v, n); cout << "Case #" << id << ": " << solve_sub(1, n, 1) << endl;}
signed main() { int T; cin >> T; fac[0] = 1; for (int i = 1; i <= T; i++) solve(i); return 0;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


