zoukankan      html  css  js  c++  java
  • P3084

    我草,刚在 typora 写完发现洛谷发题解入口被关了……心态崩了………………

    没错我就是那位被标签「差分约束」骗进来的人。

    Portal

    这题确实可以差分约束,做法也很显然,不过那样是 (mathrm O(n(n+m))) 的 SPFA,普通写可以得 60pts。不过看到 zqy 用某些邪教优化 SPFA 过了这道题,我以后有时间学一下!

    这题实际上是个不错的 DP。

    我们考虑对于一个选的牛的序列,它合法的充要条件是什么。

    很简单,就是每个区间内恰好有一头牛。那么如何简洁的体现到序列上呢?注意到恰好有一头牛意味着不能有 (0) 头或大于 (1) 头。(0) 头的话,那就是一个坐落在序列中两相邻牛之间的一个区间;大于 (1) 头的话,就是当且仅当包含了序列中连续两头牛。然后这两个条件不能有任何一个序列满足,就这样很好的贴合序列了。

    下面考虑对这个序列 DP。设 (dp_i) 表示考虑前 (i) 头牛中,若 (i) 选,则满足条件的最大的选的牛数。我们考虑将上面那两个条件融入到任意一个相邻对里面去,也就是说每次 DP 的时候只需要考虑 (i) 与决策 (j) 满足条件。

    条件一是说不能有 (j<l_k,r_k<i)。这里的 ([r_k<i]) 固定了,唯一能做的就是调整 (j)。也就是要满足 (forall r_k<i,jgeq l_k),即 (jgeqmaxlimits_{r_k<i}{l_k})

    条件二是说不能有 (l_kleq j,ileq r_k),同理推出等价于 (j<minlimits_{r_kgeq i}{l_k})

    这样我们就得出了 (j) 的上下界,除此之外还有个隐含限制 (jin[0,i))。加上去之后容易得到 (i) 的决策区间,我们要找的是区间里面的 DP 值最大值。

    考虑如何实现。上下界显然可以按 (r) 排序,然后分别从前往后、从后往前扫一遍即可求出。然后不难发现下界显然是单调不降的,上界反过来看是单调不升的,那正过来也就是单调不降的。咦,那恰好可以单调队列优化。于是复杂度线性。

    但还有一个线性对数的地方,那就是排序……不过注意到值域线性,所以可以对 (r) 桶排来实现严格的线性复杂度。然后卡了卡常,用 c++17 虐了最优解 1ms,成为了最优解。

    #include<bits/stdc++.h>
    using namespace std;
    const int inf=0x3f3f3f3f;
    char _buf[1<<23],*_st,*_ed;
    #define getchar() (_st==_ed&&(_ed=(_st=_buf)+fread(_buf,1,1<<22,stdin),_st==_ed)?EOF:*_st++)
    void read(int &x){
    	x=0;char c=getchar();
    	while(!isdigit(c))c=getchar();
    	while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar();
    }
    const int N=200000,M=100000;
    int n,m;
    int rgl[M+1],rgr[M+1];
    struct addedge{
    	int sz,val[M+1],nxt[M+1],head[N+1];
    	void init(){sz=0;memset(head,0,sizeof(head));}
    	void ae(int x,int y){
    		++sz,val[sz]=y,nxt[sz]=head[x],head[x]=sz;
    	}
    }lft;
    int l[N+2],r[N+2];
    int dp[N+2];
    int q[N+2],head,tail;
    int main(){
    	read(n),read(m);
    	lft.init();
    	for(int i=1;i<=m;++i)read(rgl[i]),read(rgr[i]),lft.ae(rgr[i],rgl[i]);
    	int now=0;
    	for(int i=1;i<=n;++i)for(int j=lft.head[i];j;j=lft.nxt[j])rgl[++now]=lft.val[j],rgr[now]=i;
    	for(int i=1,j=0,mx=0;i<=n+1;++i){
    		while(j+1<=m&&rgr[j+1]<i)mx=max(mx,rgl[++j]);
    		l[i]=mx;
    	}
    	for(int i=n+1,j=m+1,mn=inf;i;--i){
    		while(j-1&&rgr[j-1]>=i)mn=min(mn,rgl[--j]);
    		r[i]=min(i-1,mn-1);
    	}
    //	for(int i=1;i<=n+1;i++)cout<<l[i]<<" "<<r[i]<<"!
    ";
    	q[tail++]=0;
    	for(int i=1;i<=n+1;++i){
    		for(int j=r[i-1]+1;j<=r[i];++j){
    			while(head<tail&&dp[q[tail-1]]<=dp[j])--tail;
    			q[tail++]=j;
    		}
    		while(head<tail&&q[head]<l[i])++head;
    		dp[i]=head<tail?dp[q[head]]+1:-inf;
    	}
    	if(dp[n+1]<0)puts("-1");
    	else cout<<dp[n+1]-1;
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    Oracle的并发多版本 读一致性
    挖掘ADO.NET Entity框架的性能
    System.Linq扩张方法Where (Lambda表达式)
    LINQ与HQL (二)
    HQL查询中的几个函数
    LINQ 与 HQL (一)
    C# 3.0的新特性(一)
    共享锁 排他锁
    Psytopic测试
    这些地方才是真正值得去的
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/p3084.html
Copyright © 2011-2022 走看看