[BZOJ] 3126: [Usaco2013 Open]Photo

3126: [Usaco2013 Open]Photo

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 318  Solved: 162
[Submit][Status][Discuss]

Description

Farmer John has decided to assemble a panoramic photo of a lineup of his N cows (1 <= N <= 200,000), which, as always, are conveniently numbered from 1..N. Accordingly, he snapped M (1 <= M <= 100,000) photos, each covering a contiguous range of cows: photo i contains cows a_i through b_i inclusive. The photos collectively may not necessarily cover every single cow. After taking his photos, FJ notices a very interesting phenomenon: each photo he took contains exactly one cow with spots! FJ was aware that he had some number of spotted cows in his herd, but he had never actually counted them. Based on his photos, please determine the maximum possible number of spotted cows that could exist in his herd. Output -1 if there is no possible assignment of spots to cows consistent with FJ‘s photographic results.

给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点

Input

* Line 1: Two integers N and M.

* Lines 2..M+1: Line i+1 contains a_i and b_i.

Output

* Line 1: The maximum possible number of spotted cows on FJ‘s farm, or -1 if there is no possible solution.

Sample Input

5 3
1 4
2 5
3 4

INPUT DETAILS: There are 5 cows and 3 photos. The first photo contains cows 1 through 4, etc.

Sample Output

1
OUTPUT DETAILS: From the last photo, we know that either cow 3 or cow 4 must be spotted. By choosing either of these, we satisfy the first two photos as well.

HINT

Source

Gold

Analysis

这翻译质量不敢恭维= =最后还是自己看了原文

= ,=

FJ对着一队奶牛拍了一堆照片

巧合的是,每张照片中都恰好有一只奶牛

现在给你每张照片里最左边的奶牛和最右边的奶牛的编号

问你在已知条件下最多能有多少奶牛?

= 。=

这道题有点毒

先说分类:DP,单调队列优化DP

合理性:

  状态表示为 F[ i ],表示只考虑前 i 只奶牛,且第 i 只奶牛必取,特殊情况下对 F[ i ] 取特殊值以区分

  显然这样是没有后效性的

因此整个主线就基本明朗了:后面的继承前面的

那么就有一个被继承的区域,我们在这个区域里面选择最大值继承

好吧,根据题目,我们知道

  每张照片里面 仅有且必有 一只有斑点的奶牛

那么我们继承就有这么个原则了:

1. 每个区间都必须要取一个点,可以共用;

2. 每个区间内不能重复取点

那么继承从距离 i 当前区间最近的左边的那个区间开始,直到 i 当前这个区间的最左的左端点结束

你看,点 i 可以是被多个区间所包围的。

我们所说的左边最近的区间,这个时候 i 的区间指的是最小的 i 的区间,也就是上图包括 i 点的最顶上的那个小区间

那么对 i 而言,这个左边最近的区间指的是图片最左端这块飞地;而对于 i‘ ,这个左边最近指的就是 i 的区间了

在继承过程中,我们需要考虑到每一个区间,因为每一张照片都必有一个奶牛,如果这里不是最小的区间的话,很可能会漏掉某些区间

而对于最左的左端点则是包围 i 点的最大的区间的左端点,因为在一个区间里只能取一个点,而被继承的那个点是必取的

所以我们得到了百度到的一堆题解的那个核心结论:

对于点 i ,它的继承区间是:从 所在区间左边最近的区间的左端点 起,至 所在区间的最左的左端点-1 结束

对于一个点怎么迅速找到它的继承区间呢?

这= =

显然看解题人数感了

我只能做到解释其合理性

 1 for(int i = 1;i <= n+1;i++) r[i] = i-1;
 2
 3 for(int i = 1;i <= m;i++){
 4         scanf("%d%d",&a,&b);
 5         r[b] = min(r[b],a-1);
 6         l[b+1] = max(l[b+1],a);
 7 }
 8
 9 for(int i = n;i;i--)
10         r[i] = min(r[i],r[i+1]);
11
12 for(int i = 2;i <= n+1;i++)
13         l[i] = max(l[i],l[i-1]);

读入一段区间时,我们就可以更新至少两个点的继承区间值了:b+1 和 b

对于b+1,无疑区间[a,b]是它左边最近的一个区间,可以更新它的继承区间左端点;

对于b,还有[a,b]里的所有的点,他们的继承区间右端点都可以以a-1为值更新,但是这里只更新b一个点,事后可以O(n)

那么对于每个点的继承区间右端点,我们从右往左扫意味着一件事情:右边的点可以改变左边,左边的点不能更新右边

其中红线蓝线都是B的待选区间,不同时存在

当B的区间是红线的时候,B可以继承红线和黑线不重叠的地方

而A的继承区间会终止于黑线后,结果对的!

当B的区间是蓝线的时候,显然A会拿B的取值去更新,也是对的!

对于继承区间的左端点的O(n)更新,和右端点取值同理,自行思考即可

巧妙运用了某些值取值的单调性

qwq这才是算法功底好的体现啊

那么在DP的时候我们的方向就很明确了:

对于每个点,从它的继承区间里继承就行了,需要不停地扫,目测n方

而我们要取的是区间里的最大值

那么这个时候滑动窗口就发挥作用了:

如果当前这个考虑的被继承点 j 的DP值小于我们已经知道的最大值,就没必要考虑了

而对于那些已经不在继承区间里却仍然被保存的值,也没必要考虑了

因此来一波单调队列,在代码里是单调队列维护最大值,这个解释清楚了估计就很容易理解了

现在来考虑非法情况,还是就上面那张图片:

非法情况,就是 i 和 i’ 这样子的,两个区间被一个大区间包括,那么这个大区间里必须要选两个点

这个时候,我们把该点置为非法,也就是-inf

并且从这个第一次出现非法情况的 i‘ 所在的区间开始,之后每一个点都会是非法点,这个非法值会延续到最后一个点

因为对于处于大区间的点,非法区间及以后的点他们的继承区间左右端点是非法的,右端点小于左端点,包含了一个空集

你看,i’ 区间内的点,右端点是大区间的左端点-1,左端点却是 i 区间的左端点

而出了 i‘ 区间,右端点依然是大区间的左端点,左端点是 i’ 的左端点,持续非法

而出了大区间,第一个点是大区间右端点+1,叫他A点

A点的左端点是 i‘ 左端点,右端点根据初始化是A-1,而这里所有的值都是非法值

之后也显然没法再取到一个正常的值了

综上可证该算法中非法值有持续性和覆盖性

另外再说结尾是n+1的原因

我们对DP[ i ] 的定义是必取 i 的情况

但是我们不一定取 n 为斑点奶牛

所以我们要选择n+1

我怎么感觉我写的东西结果就是一堆废话qwq

Code

 1 /**************************************************************
 2     Problem: 3126
 3     User: child
 4     Language: C++
 5     Result: Accepted
 6     Time:184 ms
 7     Memory:4444 kb
 8 ****************************************************************/
 9
10 #include<cstdio>
11 #include<iostream>
12 #define maxn 202020
13 using namespace std;
14
15 int n,m,a,b,r[maxn],l[maxn],DP[maxn];
16 int que[maxn],head,tail;
17
18 int main(){
19     scanf("%d%d",&n,&m);
20
21     for(int i = 1;i <= n+1;i++) r[i] = i-1;
22
23     for(int i = 1;i <= m;i++){
24         scanf("%d%d",&a,&b);
25         r[b] = min(r[b],a-1);
26         l[b+1] = max(l[b+1],a);
27     }
28
29     for(int i = n;i;i--)
30         r[i] = min(r[i],r[i+1]);
31
32     for(int i = 2;i <= n+1;i++)
33         l[i] = max(l[i],l[i-1]);
34
35     for(int i = 1;i <= n+1;i++){
36         for(int k = l[i];k <= r[i];k++){
37             while(tail < head && DP[que[head]] <= DP[k]) head--;
38             que[++head] = k;
39         }
40
41         while(tail < head && que[tail+1] < l[i]) tail++;
42
43         if(tail == head) DP[i] = -1e9;
44         else DP[i] = DP[que[tail+1]] +1;
45     }
46
47 //  for(int i = 0;i <= n+1;i++) printf("%d ",DP[i]);
48 //  cout << endl;
49
50     if(DP[n+1] >= 0) printf("%d\n",DP[n+1]-1);
51     else printf("-1\n");
52
53     return 0;
54 }

= =

时间: 2024-10-14 10:50:50

[BZOJ] 3126: [Usaco2013 Open]Photo的相关文章

数据结构(线段树):BZOJ 3126: [Usaco2013 Open]Photo

3126: [Usaco2013 Open]Photo Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 222  Solved: 116 Description Farmer John has decided to assemble a panoramic photo of a lineup of his N cows (1 <= N <= 200,000), which, as always, are conveniently numbered

bzoj 3126: [Usaco2013 Open]Photo——单调队列优化dp

Description 给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点 Input * Line 1: Two integers N and M. * Lines 2..M+1: Line i+1 contains a_i and b_i. Output * Line 1: The maximum possible number of spotted cows on FJ's farm, or -1 if there is no possible solution. S

BZOJ 3126 Photo

好厉害啊这题. 详见http://www.cnblogs.com/zig-zag/archive/2013/05/17/3083072.html #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 200500 #define inf 0x3f3f3f3f using namespace std; int n,m,lq,rq,lazy[m

BZOJ 3314: [Usaco2013 Nov]Crowded Cows( 单调队列 )

从左到右扫一遍, 维护一个单调不递减队列. 然后再从右往左重复一遍然后就可以统计答案了. ---------------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x, c) memset(x, c, sizeof(x)) #define forea

bzoj 3312: [Usaco2013 Nov]No Change

3312: [Usaco2013 Nov]No Change Description Farmer John is at the market to purchase supplies for his farm. He has in his pocket K coins (1 <= K <= 16), each with value in the range 1..100,000,000. FJ would like to make a sequence of N purchases (1 &

BZOJ 3315: [Usaco2013 Nov]Pogo-Cow( dp )

我真想吐槽USACO的数据弱..= = O(n^3)都能A....上面一个是O(n²), 一个是O(n^3) O(n^3)做法, 先排序, dp(i, j) = max{ dp(j, p) } + w( i ) ( t <= p <= j ) 表示跳到第 i 个点, 上一个点是在 j 的最大得分, 其中t是满足条件的最小p. 我们在计算dp(i, j) (1 <= j <= i )时会发现, 随着 j 的递减, t也在不断地减小, 这样我们只要在dp过程中维护h(i, j)表示 m

Bzoj 3050: [Usaco2013 Jan]Seating(线段树裸题,然而区间修改标记下放和讨论Push_up很揪心)

题目链接 题意:开始有一个空白的区间,每次可能进行两个操作:A 将一个长度为p的区间加入一段连续空白的位置 L:一个区间恢复空白:要求出A不能进行的次数. 非常裸的线段树题目,用线段树统计最大的空白区间,每个节点需要记录当前区间的最长空白区间,从左端开始的最长空白区间,从右端开始的最长空白区间.Push_up的时候要讨论下,可以分别取[l,mid]和[mid+1,r]的最大空白区间,也可以用[l,mid]的从右端开始的最长空白区间+[mid+1,r]从左端开始的最大空白区间. 每次A的时候,就查

BZOJ 3128 Usaco2013 Open Figure Eight

题目大意:给定一个有坏点的矩阵,求能画出来的最大的"8"字形 "8"字形满足: *数字8由上下两个矩形构成. *数字8的上下两个矩形都满足至少有一个单元格在矩形内部. *数字8顶部的矩形的底边必须为底部矩形顶边的子集. *数字8只能刻在大理石完美无瑕的部分. *规定数字8的得分为上矩形和下矩形的面积的乘积,它们希望得分能达到最大. 枚举顶部矩形的底边,可以用上一行的底边O(1)转移得到,然后每行O(n^2)向左右拓展 然后枚举底部矩形的顶边,可以用下一行的顶边O(1

BZOJ 4094 Usaco2013 Dec Optimal Milking 线段树

题目大意:给定n个点排成一排,每个点有一个点权,多次改变某个点的点权并将最大点独立集计入答案,输出最终的答案 开一个线段树,每个点记录四个信息: 区间左端点不选,右端点也不选的最大值 区间左端点选择,右端点不选的最大值 区间左端点不选,右端点选择的最大值 区间左端点选择,右端点也选择的最大值 然后合并时讨论一下就行了 #include <cstdio> #include <cstring> #include <iostream> #include <algorit