安吉D5-T3
- 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
题目呈现
P9100 Miny
题目描述
枚地雷被运到 Bytau 的军事训练场,并沿一条直线埋设.每个地雷位于不同的地方,并且有自己的爆炸半径.当引爆时,地雷会自动引爆其爆炸半径内所有尚未爆炸的地雷.如果地雷 和地雷 之间的距离不超过地雷 的爆炸半径,则我们称地雷 在地雷 的爆炸半径内.
Bytomir 中士想进行一项实验.他选择了一个任意的地雷子集(也许是空的),并让这个地雷子集内的所有地雷在同时手动引爆.实验的结果是一组已经爆炸的地雷——要么是手动引爆的引起的爆炸,要么是其他地雷爆炸导致的爆炸.
Bytomir 能得到多少种可能的实验结果?如果两个实验结果中爆炸的地雷相同,则这两个实验结果是相同的.由于结果可能很大,请输出它除以 的余数.
输入格式
输入第一行包含一个整数 ,表示地雷个数.
接下来 行,每行两个整数 ,分别表示地雷的位置和爆炸半径.你可以假设 .
输出格式
输出可能的实验结果总数对 取模后的值.
输入输出样例 #1
输入 #1
40 22 03 27 4输出 #1
7说明/提示
样例 1 解释
你可以得到 种可能的实验结果:
- (空集):如果不引爆任何地雷;
- (地雷 ):如果我们只引爆地雷 ;
- :如果我们引爆地雷 和 ;
- :如果我们引爆地雷 和 ;
- :如果我们只引爆地雷 ;
- :如果我们只引爆地雷 ;
- :如果我们只引爆地雷 ;
请注意,可以通过不同的方式得到同一个实验结果——例如,如果我们引爆地雷 和 ,也会得到 的结果.
数据范围
本题采用捆绑测试
对于 的数据,保证 .
对于 的数据,保证 ,.
先来解决小数据.
由于小数据数据范围较少,考虑 dp,这里定义 dp[i] 表示考虑到第 个地雷,且第 个地雷不被引爆的方案数,这样 dp 可以使得第 个地雷(及以前的所有地雷)都不会引爆第 个地雷之后的所有地雷,从而满足无后效性.
显然,有初始状态:dp[0] = 1,即不选择地雷,则存在没有地雷爆炸这一种情况.
考虑转换方程:dp[i] 可以由 dp[j] 转换而来(),当且仅当手动引爆第 个地雷时,地雷 和 都不会被引爆.
为了满足这两颗地雷都不会被引爆,我们需要保证:
- 地雷 左边第一个会引爆 的地雷不在区间 之中.(保证地雷 不会被引爆)
- 地雷 右边第一个会引爆 的地雷不在区间 之中.(保证地雷 不会被引爆)
即,设地雷 左边第一个会引爆 的地雷为 ,地雷 右边第一个会引爆 的地雷为 .则符合条件的 满足:
,从 开始向前遍历直到 ,寻找合法的 加入贡献即可.
在计算 时,从 开始向前遍历,找到第一个 的地雷,.同理,在计算 时,从 开始向前遍历,找到第一个 的地雷,.
在统计最终状态时,可以引入哨兵,在地雷序列首位和末尾各增加一个距离无限远、爆炸半径无限远(可以炸到另外一个哨兵)的哨兵地雷 和 .从而使得 , 和 都不为空,同时,dp[n+1] 还能收集到所有未贡献状态(即 的状态 dp[j]).
最终状态即为 dp[n+1].
标程
#include <bits/stdc++.h>using namespace std;#define int long longconst int N = 3e5 + 100;const int MOD = 1e9 + 7;int n, a[N], r[N], dp[N], L[N], R[N];
signed main() {#ifdef ONLINE_JUDGE freopen("landmine.in", "r", stdin); freopen("landmine.out", "w", stdout);#endif cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i] >> r[i]; } a[n + 1] = 2e18; r[n + 1] = 4e18 + 10; // LLONG_MAX 约为 9e18 a[0] = -2e18; r[0] = 4e18 + 10; for (int i = 0; i <= n + 1; i++) { for (int j = 0; j < i; j++) { if (a[j] + r[j] >= a[i]) { L[i] = j; } } } memset(R, 0x3f, sizeof(R)); for (int i = 0; i <= n + 1; i++) { for (int j = i + 1; j <= n + 1; j++) { if (a[j] - r[j] <= a[i]) { R[i] = min(j, R[i]); } } } dp[0] = 1; for (int i = 0; i <= n + 1; i++) { for (int j = L[i]; j < i; j++) { if (i <= R[j]) { (dp[i] += dp[j]) %= MOD; } } } cout << dp[n + 1]; return 0;}这样的复杂度是 的,考虑如何优化.
首先,预处理 和 可以使用 ST 表和二分进行优化.以计算 为例,具体来说,对于区间 ,二分可能的子区间 ,对于该子区间,求出 的最大值(即每个地雷最右影响位置)是否比 大(或等于),如果是,说明 在该子区间内,在该子区间内继续二分,否则,在相反的子区间 内进行二分. 的处理和 相类似.
ST 表实现如下:
struct ST { int *p; int fmax[32][N]; int fmin[32][N]; ST(int *p) : p(p) {} void init(int size) { for (int i = 0; i <= size; i++) { fmax[0][i] = p[i]; fmin[0][i] = p[i]; } for (int j = 1; j <= 30; j++) { for (int i = 0; i + (1 << (j - 1)) <= size; i++) { fmax[j][i] = max(fmax[j - 1][i], fmax[j - 1][i + (1 << (j - 1))]); fmin[j][i] = min(fmin[j - 1][i], fmin[j - 1][i + (1 << (j - 1))]); } } } int mini(int l, int r) { int len = log(r - l + 1) / log(2); return min(fmin[len][l], fmin[len][r - (1 << len) + 1]); } int maxi(int l, int r) { int len = log(r - l + 1) / log(2); return max(fmax[len][l], fmax[len][r - (1 << len) + 1]); }};
ST l_st(l_lim), r_st(r_lim);预处理实现如下:
for (int i = 0; i <= n + 1; i++) { l_lim[i] = a[i] - r[i]; r_lim[i] = a[i] + r[i];}
l_st.init(n + 1), r_st.init(n + 1);
for (int i = 0; i <= n + 1; i++) { int l = 0, r = i - 1, ans = 0; while (l <= r) { int mid = (l + r) / 2; if (r_st.maxi(mid, r) >= a[i]) { ans = mid; l = mid + 1; } else { r = mid - 1; } } L[i] = ans;}for (int i = 0; i <= n + 1; i++) { int l = i + 1, r = n + 1, ans = n + 1; while (l <= r) { int mid = (l + r) / 2; if (l_st.mini(l, mid) <= a[i]) { ans = min(ans, mid); r = mid - 1; } else { l = mid + 1; } } R[i] = ans;}再考虑如何优化 dp.
dp 的转移方程中涉及到区间加和的问题,因此可以将 数组放入树状数组优化.在每一次计算 时,计算 之和,再减去所有不满足 的 .
怎么减去呢?要减去的 满足 ,显然,当一项需要被减去之后(),该项就不会再对之后的计算产生贡献().因此,在每一次计算完 之后,将所有的 满足 的 在树状数组中删除(即设为 0)(查找满足要求的 可以使用 map),这样,在后续进行计算时,该项就不会被计入贡献.
map<int, vector<int>> r_R;
// 树状数组实现struct BIT { int c[N]; int size; int lowbit(int i) { return i & (-i); } BIT() { memset(c, 0, sizeof(c)); }
void init(int size) { this->size = size; } void upd(int p, int v) { p++; // 转为 1-based for (int i = p; i <= size; i = i + lowbit(i)) { (c[i] += v + MOD) %= MOD; } } int qry(int p) { p++; // 转为 1-based int ans = 0; for (int i = p; i >= 1; i -= lowbit(i)) { (ans += c[i]) %= MOD; } return ans; } int qry(int l, int r) { return qry(r) - qry(l - 1); }};BIT bit;main 函数内:
bit.upd(0, 1);dp[0] = 1;for (int i = 1; i <= n + 1; i++) { (dp[i] += bit.qry(L[i], i - 1)) %= MOD; bit.upd(i, dp[i]); vector<int> &vec = r_R[i]; for (int j = 0; j < vec.size(); j++) { int ori = bit.qry(vec[j], vec[j]); bit.upd(vec[j], -ori); }}标程
#include <bits/stdc++.h>using namespace std;#define int long longconst int N = 3e5 + 100;const int MOD = 1e9 + 7;int n, a[N], r[N], dp[N], L[N], R[N], l_lim[N], r_lim[N];map<int, vector<int>> r_R;
struct ST { int *p; int fmax[32][N]; int fmin[32][N]; ST(int *p) : p(p) {} void init(int size) { for (int i = 0; i <= size; i++) { fmax[0][i] = p[i]; fmin[0][i] = p[i]; } for (int j = 1; j <= 30; j++) { for (int i = 0; i + (1 << (j - 1)) <= size; i++) { fmax[j][i] = max(fmax[j - 1][i], fmax[j - 1][i + (1 << (j - 1))]); fmin[j][i] = min(fmin[j - 1][i], fmin[j - 1][i + (1 << (j - 1))]); } } } int mini(int l, int r) { int len = log(r - l + 1) / log(2); return min(fmin[len][l], fmin[len][r - (1 << len) + 1]); } int maxi(int l, int r) { int len = log(r - l + 1) / log(2); return max(fmax[len][l], fmax[len][r - (1 << len) + 1]); }};
struct BIT { int c[N]; int size; int lowbit(int i) { return i & (-i); } BIT() { memset(c, 0, sizeof(c)); }
void init(int size) { this->size = size; } void upd(int p, int v) { p++; // 转为 1-based for (int i = p; i <= size; i = i + lowbit(i)) { (c[i] += v + MOD) %= MOD; } } int qry(int p) { p++; // 转为 1-based int ans = 0; for (int i = p; i >= 1; i -= lowbit(i)) { (ans += c[i]) %= MOD; } return ans; } int qry(int l, int r) { return qry(r) - qry(l - 1); }};
ST l_st(l_lim), r_st(r_lim);BIT bit;
signed main() {#ifdef ONLINE_JUDGE freopen("landmine.in", "r", stdin); freopen("landmine.out", "w", stdout);#endif cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i] >> r[i]; } a[n + 1] = 2e18; r[n + 1] = 4e18 + 10; a[0] = -2e18; r[0] = 4e18 + 10; for (int i = 0; i <= n + 1; i++) { l_lim[i] = a[i] - r[i]; r_lim[i] = a[i] + r[i]; }
l_st.init(n + 1), r_st.init(n + 1); bit.init(n + 1);
for (int i = 0; i <= n + 1; i++) { int l = 0, r = i - 1, ans = 0; while (l <= r) { int mid = (l + r) / 2; if (r_st.maxi(mid, r) >= a[i]) { ans = mid; l = mid + 1; } else { r = mid - 1; } } L[i] = ans; } for (int i = 0; i <= n + 1; i++) { int l = i + 1, r = n + 1, ans = n + 1; while (l <= r) { int mid = (l + r) / 2; if (l_st.mini(l, mid) <= a[i]) { ans = min(ans, mid); r = mid - 1; } else { l = mid + 1; } } R[i] = ans; r_R[R[i]].push_back(i); } bit.upd(0, 1); dp[0] = 1; for (int i = 1; i <= n + 1; i++) { (dp[i] += bit.qry(L[i], i - 1)) %= MOD; bit.upd(i, dp[i]); vector<int> &vec = r_R[i]; for (int j = 0; j < vec.size(); j++) { int ori = bit.qry(vec[j], vec[j]); bit.upd(vec[j], -ori); } } cout << dp[n + 1]; return 0;}文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!


