这道题是道非常好的动规题,不难但思考过程受益很大。
[9/25日更新:鸽了俩个月回来补档了]
题目描述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(1 <= N <= 200,000)头奶牛制作一张全家福照片,N头奶牛编号1到N。 于是约翰拍摄了M(1 <= M <= 100,000)张照片,每张照片都覆盖了连续一段奶牛:第i张照片中包含了编号a_i 到 b_i的奶牛。但是这些照片不一定把每一只奶牛都拍了进去。 在拍完照片后,约翰发现了一个有趣的事情:每张照片中都有且仅有一只身上带有斑点的奶牛。约翰意识到他的牛群中有一些斑点奶牛,但他从来没有统计过它们的数量。 根据照片,请你帮约翰估算在他的牛群中最多可能有多少只斑点奶牛。如果无解,输出“-1”。 输入输出格式输入格式: * Line 1: Two integers N and M. * Lines 2..M+1: Line i+1 contains a_i and b_i. 输出格式: * Line 1: The maximum possible number of spotted cows on FJ‘s farm, or -1 if there is no possible solution. |
我们看到题目中有个很重要的条件:“每张照片中都有且仅有一只身上带有斑点的奶牛”。
一:用f[i]表示选第i头牛时最大牛数,假设f[i]是由某个j转移过来:
1.区间最多只有一头:
考虑这个情况,则j必须在i所在的所有区间中最左的那个端点之前,记为j<=r[i],r[i]表示i所在所有区间的最左端点;
2.区间最少只有一头:
考虑这个情况,则i覆盖的所有区间中,除i这个位置以外都不能放牛,所以极有可能使得在i左方的某一个区间被i所在的某一个区间覆盖或者相交的情况,考虑最近的那个,即所有 区间范围在i左侧的区间 中左端点最靠近i(即最大)的那个点,记为l[i],有j>=l[i]。
综上,我们得出f[i]=max{f[j]+1,l[i]<=j<=r[i]}。
二:我们考虑如何维护l与r:
输入区间的时候,设区间为[a,b],可以确定在b这个点,有一点a-1可以确定为b的一个左值(但不知道是否为最左的那个),暂且记为r[b]=a-1。同理,对于点b+1,a也有可能是它的一个右值(也是暂定),记为l[b+1]=a。
而我们知道,一个区间最小不会小于只包含一个数字,所以考虑r[i],还可以提前一步令r[i]=i-1,总之取最小值就可以了;
现在我们去求是否有更优答案:
对于一个点i,和i+1,我们知道,如果一个区间右端为i+1,那么这个区间有两个可能:
1.不过i 这个时候区间就是一个数,r[i+1]=i。
2.过i 这个时候,r[i+1]有可能比r[i]还小。
综上两点,其实只要取r[i]=min{r[i],r[i+1]}即可。(情况1根本不用考虑好吗。。。)
l[i]类似,在此不做赘述,l[i]=max{l[i],l[i-1]}。
接着我们用单调队列来处理这个东西:
我们在l[i]到r[i]的范围内,找到最大的f[x]更新。
参考了一下大佬的代码,这里有一些小细节:比如可以用n+1来表示是否有解,单调队列的写法等。
#include<bits/stdc++.h> using namespace std; int read(){ int x=0,f=1; char c=getchar(); while(!isdigit(c)){ if(c==‘-‘) f=-1; c=getchar(); } while(isdigit(c)){ x=(x<<1)+(x<<3)+(c^48); c=getchar(); } return x*f; } const int N=2e5+10; int n,m; int l[N],r[N],f[N],q[N]; struct cow{ int a,b; }t[N]; int main(){ n=read();m=read(); for(int i=1;i<=n+1;i++) r[i]=i-1; for(int i=1;i<=m;i++){ t[i]=(cow){read(),read()}; l[t[i].b+1]=max(l[t[i].b+1],t[i].a); r[t[i].b]=min(r[t[i].b],t[i].a-1); } for(int i=n;i>=1;i--){ r[i]=min(r[i],r[i+1]); } for(int i=2;i<=n+1;i++){ l[i]=max(l[i],l[i-1]); } int head=1,tail=1; int pos=1; for(int i=1;i<=n+1;i++){ while(pos<=r[i]&&pos<=n){ if(f[pos]==-1){ pos++; continue; } while(head<=tail&&f[pos]>f[q[tail]]) tail--; q[++tail]=pos; pos++; } while(head<=tail&&q[head]<l[i]) head++; if(head<=tail) f[i]=f[q[head]]+(i!=n+1?1:0); else f[i]=-1; } printf("%d",f[n+1]); return 0; }
原文地址:https://www.cnblogs.com/Nelson992770019/p/11179109.html