[Jzoj] 3233. 照片

题目大意

F a r m e r J o h n Farmer John FarmerJohn决定为他的 N N N头排列好的奶牛 ( 1 &lt; = N &lt; = 200 , 000 ) (1 &lt;= N&lt;= 200,000) 1<=N<=200,000做一张全景合照。这 N N N头奶牛分别以 1.. N 1..N 1..N进行编号。他一共拍了 M ( 1 &lt; = M &lt; = 100 , 000 ) M(1&lt;= M &lt;=100,000) M1<=M<=100,000张相片,每张相片都只包含有一部分位置连续的奶牛:第i张照片涵盖着编号从 a i a_i ai b i b_i bi的所有奶牛。当然,这些照片合起来并不保证包含所有的牛。

F a r m e r J o h n Farmer John FarmerJohn拍摄完所有的相片后,注意到一个很有趣的现象:他拍的每张照片中有且仅有一只奶牛身上有斑点。 F a r m e r J o h n Farmer John FarmerJohn知道他的奶牛中有一部分是身上有斑点的,但他从来没有数过这种奶牛的数目。请根据 F a r m e r J o h n Farmer John FarmerJohn的这些照片,确定可能出现的斑点牛的最大的数目;若从 F a r m e r J o h n Farmer John FarmerJohn的照片中无法推测斑点牛的数目,则输出 − 1 -1 1

题目解析

差分约束,可以过的

这里讲 D P DP DP,单调队列优化。
首先,设 F [ i ] F[i] F[i]表示 i i i必须放斑点牛,前 i i i个牛中最多能放多少个斑点牛.
我们可以考虑出上一个斑点牛放在哪里,
l [ i ] l[i] l[i]表示放了斑点牛在 i i i后,上一个斑点牛最远可以放在哪里。即 i i i所在区间的上一个区间的最左端。
r [ i ] r[i] r[i]表示放了斑点牛在 i i i后,上一个斑点牛最近可以放在哪里。即 i i i所在区间的上一个区间的最右端。
给个区间 [ x , y ] [x,y] [x,y] x − 1 x−1 x1去更新 r [ y ] r[y] r[y],用 x x x去更新 l [ y + 1 ] l[y+1] l[y+1]。最后 l l l取前缀最大值, r r r取后缀最小值。

于是 F [ i ] = M a x ( F [ j ] ) + 1 F[i]=Max(F[j])+1 F[i]=Max(F[j])+1 ( l [ i ] &lt; = j &lt; = r [ i ] ) (l[i]&lt;=j&lt;=r[i]) (l[i]<=j<=r[i])
考虑到 l , r l,r l,r数组满足单调性,可以使用单调队列优化

#include<bits/stdc++.h>
using namespace std;
const int N=201000;
int n,m;
int l[N],r[N],f[N];
deque<int> q;
int main()
{
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=n+1;i++) r[i]=i-1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        l[y+1]=max(l[y+1],x);
        r[y]=min(r[y],x-1);
    }
    memset(f,0,sizeof(f));
    for(int i=2;i<=n+1;i++) 
	 l[i]=max(l[i],l[i-1]);
    for(int i=n;i>=1;i--)
	 r[i]=min(r[i+1],r[i]);
    q.push_front(0);
	int j=1;
    for(int i=1;i<=n+1;i++)
    {
        while(j<=r[i]&&j<=n)
        {
            if(f[j]==-1)
            {
                j++;
                continue;
            }
            while(!q.empty()&&f[j]>f[q.back()])
			 q.pop_back();
            q.push_back(j);
            j++;
        }
        while(q.front()<l[i]&&!q.empty()) 
		 q.pop_front();
        if(!q.empty())
        {
          if(i!=n+1)
           f[i]=f[q.front()]+1;
          else
           f[i]=f[q.front()];
		}
        else f[i]=-1;
    }
    printf("%d\n",f[n+1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值