[USACO13OPEN] Photo

这是一个很棒的动态规划题

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.

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.
Examples of input and output

Input #1
5 3
1 4
2 5
3 4
Output #1
1

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

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.




問題の解

看到讨论版说可以用差分约束可以做,但是因为不会的缘故所以我没有选择差分约束。

概括一下题目大意,给定了一些区间:\(R_i=[L_i,R_i]\),每个区间上有且仅有一个点。问最多几个点。


emmmmmmmmmmm……
容易得到状态转移方程:

(其实真的一点都不容易,我想了不知道多久……最后还偷偷看了几行题解)
\[ f[i]=f_{max}[j]+1,j \in \{ 某种条件 \} \]
其中,\(f[i]\)表示在\(i\)处放一个点,算上这一个点,从\(1\)\(i\)位置最多放置几个点。



很直接的方程,问题在于这个“某种条件”。

考虑“某种条件”的两个端点值。


\(1^{\circ}\)左边最远去哪里?
我们希望\(j\)的值越小越好,但是不能太小。比如说从\(i\)\(j\)居然越过了某个整个的区间,这是不可能的。因为根据状态转移方程的定义,\((j,i]\)中间是不可以放置点的。
所以,大概可以知道这个左端点的值最小可以去到一个:

  1. 不覆盖\(i\)的;
  2. 左端点值尽量大的;

区间的左端点。把它记为\(maxl[i]\).


\(2^{\circ}\)右边最远去哪里?
我们希望\(j\)的值越大越好,但是不能太大。比如说从\(i\)\(j\)居然在同一个区间里,这是不可能的。因为同一个区间有且仅有一个点。
所以,大概可以知道这个右端点的值最大可以去到一个:

  1. 覆盖\(i\)的;
  2. 左端点值尽量小的;

区间的左端点的再左边一个单位的位置。把它记为\(minl[i]\).

关于\(maxl[i]\)\(minl[i]\)的求法,递推即可。

读入\([L_i,R_i]\)的时候:

    minl[r]=min(minl[r],l);
    maxl[r+1]=max(maxl[r+1],l);

然后扫描:

    for(int i=1;i<=n+1;i++)
        maxl[i]=max(maxl[i-1],maxl[i]);
    for(int i=n ;i>=0;i--)
        minl[i]=min(minl[i+1],minl[i]);

因为我们每次都是取\([maxl[i],minl[i])\)的最大值进行状态转移,还注意到\(maxl[]\)\(minl[]\)都是递增数列;

这就非常开心了,直接上单调队列优化。

关于非法情况,即要转移给\(f[i]\)\(j\)不存在,也即,队列空,那么\(f[i]=-1\);

最后弄个假的点\(n+1\),注意这个点是假的所以再状态转移的时候不能+1.

最终的状态转移方程:
\[ f[i]=f[Q.front()]+(!(i==n+1)),\ Q.front() \ is \ the \ maximum \ value \ in [maxl[i],minl[i]). \]

*关于这个状态转移方程其实我仍存疑问。在OJ的评测中,写成\(f[i]=f[Q.back()]+(!(i==n+1))\)也是可以AC的。这可能是一个很傻的问题,但是知道真相的人请务必留言。

码字好累啊……




コード:

#include<bits/stdc++.h>
using namespace std;
const int N=200005,INF=0x3f3f3f3f,M=100005;
int maxl[N],minl[N],f[N];
int tot=0,ans=0;
int n,m;
bool ban[N];
deque<int> Q;
inline int read()
{
    char c=getchar();int x=0;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x;
}


int main()
{
    n=read();m=read();
    for(int i=1;i<=n+1;i++)minl[i]=i;
    for(int i=1;i<=m;i++)
    {
        int l=read();
        int r=read();
        minl[r]=min(minl[r],l);
        maxl[r+1]=max(maxl[r+1],l);
    }
    for(int i=1;i<=n+1;i++)
        maxl[i]=max(maxl[i-1],maxl[i]);
    for(int i=n ;i>=0;i--)
        minl[i]=min(minl[i+1],minl[i]);
    
    int j=1;
    Q.push_back(0);
    for(int i=1;i<=n+1;i++)
    {
        for(;j<minl[i];j++)
        {
            if(f[j]!=-1)
            {
                while(!Q.empty()&&f[Q.back()]<f[j])Q.pop_back();
                Q.push_back(j);
            }
        }
        while(!Q.empty()&&Q.front()<maxl[i])Q.pop_front();
        if(!Q.empty())
            f[i]=f[Q.front()]+(!(i==n+1));
        else
            f[i]=-1;
    }
    cout<<f[n+1];
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kion/p/11819379.html