题意:你有n个俄罗斯套娃,已知每个套娃的容积和体积,问有多少个子集满足以下条件:

1:这个子集是一个极大子集,即不能再添加其它的套娃到这个子集里。

2:子集的套娃之间的间隙和最小。

思路1:线段树优化DP:

首先把套娃按照容积为第一优先级,体积为第二优先级,从小到大排序。设ans[i].second为第i个套娃在最外层,间隙最小的极大子集的数目,ans[i].first为最小的间隙,我们来执行转移:假设第i个套娃的体积是r,容积比r大的第一个套娃的容积是l1, 体积是r1, 那么容易发现,所有容积在[l1, r1 - 1]范围内的套娃都可能由当前状态转移。我们可以通过线段树的特殊标记来执行转移,我在线段树维护了3个标记:容积,最小间隙,数目。在更新懒标记时,判断下放的最小间隙 - 容积是否比当前的小即可。这样通过线段树的单点查询就可以得到对应的DP状态。

代码:

#include <bits/stdc++.h>
#define LL long long
#define pLL pair<LL, LL>
#define pii pair<int, int>
#define ls (o << 1)
#define rs (o << 1 | 1)
using namespace std;
const int maxn = 200010;
const LL mod = 1000000007;
pii a[maxn];
int val[maxn];
struct Seg {
pLL lz;
int pos;
};
Seg tr[maxn * 4];
pLL ans[maxn];
bool v[maxn];
void maintain(int o, pLL tmp, int p) {
if(tmp.first - p < tr[o].lz.first - tr[o].pos) {
tr[o].lz = tmp, tr[o].pos = p;
} else if(tmp.first - p == tr[o].lz.first - tr[o].pos) {
tr[o].lz.second = (tr[o].lz.second + tmp.second) % mod;
}
}
void pushdown(int o) {
if(tr[o].lz.second != 0) {
maintain(ls, tr[o].lz, tr[o].pos);
maintain(rs, tr[o].lz, tr[o].pos);
tr[o].lz = make_pair(1e18, 0ll);
tr[o].pos = 0;
}
}
void build(int o, int l, int r) {
if(l == r) {
if(val[l]) {
tr[o].lz = make_pair(0, val[l]);
tr[o].pos = 0;
} else {
tr[o].lz = make_pair(1e18, 0);
tr[o].pos = 0;
}
return;
}
tr[o].lz = make_pair(1e18, 0);
tr[o].pos = 0;
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}
pLL query(int o, int l, int r, int p) {
if(l == r) {
ans[l] = make_pair(tr[o].lz.first + a[l].first - tr[o].pos, tr[o].lz.second);
return ans[l];
}
pushdown(o);
int mid = (l + r) >> 1;
if(p <= mid) return query(ls, l, mid, p);
else return query(rs, mid + 1, r, p);
}
void update(int o, int l, int r, int ql, int qr, int p, pLL tmp) {
if(l >= ql && r <= qr) {
maintain(o, tmp, p);
return;
}
pushdown(o);
int mid = (l + r) >> 1;
if(ql <= mid) update(ls, l, mid, ql, qr, p, tmp);
if(qr > mid) update(rs, mid + 1, r, ql, qr, p, tmp);
}
int main() {
int n;
LL mi = 1e18;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i].second, &a[i].first);
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++) {
if(a[i].first >= mi) break;
val[i] = 1;
mi = min(mi, (LL)a[i].second);
}
build(1, 1, n);
mi = 1e18;
for (int i = 1; i <= n; i++) {
pLL tmp = query(1, 1, n, i);
ans[i] = tmp;
int pos1 = lower_bound(a + 1, a + 1 + n, make_pair(a[i].second, a[i].second)) - a;
int pos2 = lower_bound(a + 1, a + 1 + n, make_pair(a[pos1].second, a[pos1].second)) - a - 1;
if(pos1 <= n)
update(1, 1, n, pos1, pos2, a[i].second, tmp);
if(pos1 <= n) {
v[i] = 1;
}
}
for (int i = 1; i <= n; i++) {
if(v[i]) continue;
mi = min(mi, ans[i].first);
}
LL res = 0;
for (int i = 1; i <= n; i++) {
if(v[i]) continue;
if(ans[i].first == mi) res = (res + ans[i].second) % mod;
}
printf("%lld\n", res);
}

思路2:(来自PinkRabbit)刚才的做法虽然能过,但是对题目的性质发掘的不够完全。我们把思维转变一下:我们不妨看一下有哪些状态可能转移到当前状态,容易发现在排序之后能转移到当前状态的状态是一个前缀,那么我们在转移的时候可以采用2个指针这种做法。那么新的问题来了,怎么知道这些前缀状态中哪些是可以真正转移到当前状态呢?设f[i]为到第i个套娃的最小间隙,ans[i]为最小间隙的数目,a[i]为第i个套娃的体积,b[i]为容积,那么在那些前缀最状态中(假设是状态t),f[t] - a[t]最小的状态才可以像当前状态转移。所以在两个指针的过程中只需维护f[t] - a[t]的最小值即可,然后执行转移。

代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 200010;
const LL mod = 1000000007;
int a[maxn], b[maxn], p1[maxn], p2[maxn];
LL ans[maxn];
int f[maxn];
bool cmp1(int x, int y) {
return a[x] < a[y];
}
bool cmp2(int x, int y) {
return b[x] < b[y];
}
int main() {
int n, ed = -1;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i], &b[i]);
p1[i] = p2[i] = i;
ed = max(ed, b[i]);
}
sort(p1 + 1, p1 + 1 + n, cmp1);
sort(p2 + 1, p2 + 1 + n, cmp2);
int pos = 1;
int mi = 0, sum = 1;
for (int i = 1; i <= n; i++) {
int now = p2[i];
while(pos <= n && a[p1[pos]] <= b[now]) {
int t = p1[pos];
if(f[t] - a[t] < mi) {
mi = f[t] - a[t];
sum = ans[t];
} else if(f[t] - a[t] == mi) {
sum = (sum + ans[t]) % mod;
}
pos++;
}
f[now] = b[now] + mi;
ans[now] = sum;
}
LL res = 0;
mi = 1e9;
for (int i = 1; i <= n; i++) {
if(a[i] <= ed) continue;
if(f[i] < mi) {
mi = f[i];
res = ans[i];
} else if(f[i] == mi) {
res = (res + ans[i]) % mod;
}
}
printf("%lld\n", res);
}

  

Codeforces 1197E Culture Code DP的更多相关文章

  1. [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路)

    [Codeforces 1197E]Culture Code(线段树优化建图+DAG上最短路) 题面 有n个空心物品,每个物品有外部体积\(out_i\)和内部体积\(in_i\),如果\(in_i& ...

  2. CodeForces 543A - Writing Code DP 完全背包

    有n个程序,这n个程序运作产生m行代码,但是每个程序产生的BUG总和不能超过b, 给出每个程序产生的代码,每行会产生ai个BUG,问在总BUG不超过b的情况下, 我们有几种选择方法思路:看懂了题意之后 ...

  3. CodeForces 544C (Writing Code)(dp,完全背包)

    题意:有n个程序员,要协作写完m行代码,最多出现b个bug,第i个程序员每写一行代码就会产生a[i]个bug,现在问,这n个人合作来写完这m行代码,有几种方案使得出的bug总数不超过b(题中要求总方案 ...

  4. Educational Codeforces Round 69 (Rated for Div. 2) E. Culture Code

    Educational Codeforces Round 69 (Rated for Div. 2) E. Culture Code 题目链接 题意: 给出\(n\)个俄罗斯套娃,每个套娃都有一个\( ...

  5. [Codeforces 1201D]Treasure Hunting(DP)

    [Codeforces 1201D]Treasure Hunting(DP) 题面 有一个n*m的方格,方格上有k个宝藏,一个人从(1,1)出发,可以向左或者向右走,但不能向下走.给出q个列,在这些列 ...

  6. Codeforces Round #302 (Div. 2).C. Writing Code (dp)

    C. Writing Code time limit per test 3 seconds memory limit per test 256 megabytes input standard inp ...

  7. Codeforces 1163D Mysterious Code(AC自动机+DP)

    用 AC自动机 来做有点想不到,捞一手就是学一手. 设 dp[ i ][ j ] 表示字符串 c 中的第 i 位到字典树上节点 j 的最大值是多少, word[ j ] 表示在节点 j 下对答案修改的 ...

  8. 背包DP || Codeforces 544C Writing Code

    程序员写bug的故事23333 题意:n个程序员,一共写m行程序,最多产生b个bug,问方案数 思路:f[i][j]表示写了i行,产生了j个bug的方案数,因为每个人都是可以独立的,所以i循环到n都做 ...

  9. Educational Codeforces Round 69 E - Culture Code (最短路计数+线段树优化建图)

    题意:有n个空心物品,每个物品有外部体积outi和内部体积ini,如果ini>outj,那么j就可以套在i里面.现在我们要选出n个物品的一个子集,这个子集内的k个物品全部套在一起,且剩下的物品都 ...

随机推荐

  1. jmeter 参数化1_User Parameters(用户参数)

    参数化:是自动化测试脚本的一种常用技巧,可将脚本中的某些输入使用参数来代替,如登录时利用GET/POST请求方式传递参数的场景,在脚本运行时指定参数的取值范围和规则. 脚本在运行时,根据需要选取不同的 ...

  2. CSS9:动态 REM-手机专用的自适应方案

    CSS9:动态 REM-手机专用的自适应方案 动态 REM是手机专用,是如何适配所有手机的方案,不是响应式方案,例如 : taobao.com 是专门的PC端m.taobao.com 是专门的手机端, ...

  3. 洛谷4843 BZOJ2502 清理雪道

    有源汇有上下界的最小可行流. YY一下建图应该很好搞吧(? 就是对于每个雪道都是[1,inf]然后源点到所有点都是[0,inf]所有点到汇点都是[0,inf] 这样的话跑一个有源汇上下界最小可行流就可 ...

  4. oracle中分页函数写法

    1.常见的分页查询语句: 查询21到40条之间的数据:SELECT *FROM (select UI.*,ROWNUM RN FROM (select * from user_info) AWHERE ...

  5. 【leetcode】553. Optimal Division

    题目如下: 解题思路:这是数学上的一个定理.对于x1/x2/x3/..../xN的序列,加括号可以得到的最大值是x1/(x2/x3/..../xN). 代码如下: class Solution(obj ...

  6. 提高wifi速度的设置办法

    系列的提高wifi速度的设置办法 在DNS一栏有你们家的地址,在你们家的地址前输入“114.114.114.114”并以“,”结尾(注意:要用英文输入法哦.) 设置完后点击左上角的“无线局域网”回到初 ...

  7. 4412 chmod权限

    chmod权限 使用命令"man 2 chmod"学习chmod函数• int chmod(const char *path, mode_t mode);– 参数*path:文件路 ...

  8. paper 157:文章解读--How far are we from solving the 2D & 3D Face Alignment problem?-(and a dataset of 230,000 3D facial landmarks)

    文章:How far are we from solving the 2D & 3D Face Alignment problem?-(and a dataset of 230,000 3D ...

  9. Oil Deposits( hdu1241

    http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82828#problem/L Oil Deposits Time Limit:1000MS ...

  10. 20 October in ss

    Contest A: sum 快速读. B: 鬼谷子的钱袋(coin) 贪心. 按照类似二进制的方式准备钱袋:1, 2, 4, 8, ... 以此装入的钱袋数目记为 \(N\). 如果最后剩余不足以凑 ...