题目描述
-
Farmer John决定为他的N头排列好的奶牛(1 <= N<= 200,000)做一张全景合照。这N头奶牛分别以1…N进行编号。他一共拍了M(1<= M <=100,000)张相片,每张相片都只包含有一部分位置连续的奶牛:第i张照片涵盖着编号从a_i到b_i的所有奶牛。当然,这些照片合起来并不保证包含所有的牛。
-
Farmer John拍摄完所有的相片后,注意到一个很有趣的现象:他拍的每张照片中有且仅有一只奶牛身上有斑点。 FJ知道他的奶牛中有一部分是身上有斑点的,但他从来没有数过这种奶牛的数目。请根据FJ的这些照片,确定可能出现的斑点牛的最大的数目;若从FJ的照片中无法推测斑点牛的数目,则输出-1。
数据范围
错解——并查集
- 这鬼算法耗了我一个下午的脑力,结果发现这竟然是错误的!
- 这道题看起来与奇偶游戏似乎有相似的妙处,但其实这两者有本质的区别。具体就不说了,感性理解一下。
半正解——差分约束
- 这算法对我来说很陌生,好像也就打过那么一两次。
- 它其实就是利用“数形结合的思想”,把几个数之间的不等关系转换成一个图。
- 如果 ,则从 向 连一条权值为 的边即可。
- 这道题有三个不等关系,我们设 代表前 个位置斑点牛的个数。则每个区间 代表 。
- 且对于任意 , 。
- 答案即使从 到 的最短路。
- 但这道题特别坑,会卡SPFA。
- 所以要加一些鬼畜优化。
正解——DP
- 这方法没什么好说的,就是找出如果这个位置是斑点牛,前一个斑点牛的范围 ,然后DP就好。
- 如果你有闲心,可以用线段树去维护DP值。
- 听说这道题可以用单调队列优化,这样时间复杂度更优秀。
代码(差分约束)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{
int x,y,c,next;
}a[1100000];int len,last[210000];
int d[210000],cnt[210000];bool v[210000];
int list[210000],head,tail,maxn;
void ins(int x,int y,int c){
a[++len].x=x;a[len].y=y;a[len].c=c;
a[len].next=last[x];last[x]=len;
}
int sum=0;
bool spfa(int st){
memset(d,60,sizeof(d));d[st]=0;
memset(cnt,0,sizeof(cnt));
memset(v,true,sizeof(v));v[st]=false;
list[1]=st;head=1;tail=2;if(tail==maxn+1) tail=1;
while(head!=tail){
int x=list[head];
sum++;if(sum>5000000) {d[maxn]=-1;break;}
for(int k=last[x];k;k=a[k].next){
int y=a[k].y;
if(d[y]>d[x]+a[k].c){
d[y]=d[x]+a[k].c;
cnt[y]=cnt[x]+1;
if(cnt[y]>=maxn)
return false;
if(v[y]){
v[y]=false;
list[tail]=y;
if(d[list[tail]]<d[list[head+1]]) swap(list[tail],list[head+1]);
tail++;if(tail==maxn+1) tail=1;
}
}
}
head++;if(head==maxn+1) head=1;
v[x]=true;
}
return true;
}
int main()
{
int n,m;scanf("%d%d",&n,&m);maxn=n;
len=0;memset(last,0,sizeof(last));
for(int i=1;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r);
ins(l-1,r,1);
ins(r,l-1,-1);
}
for(int i=0;i<n;i++) ins(i,i+1,1),ins(i+1,i,0);
bool pd=spfa(0);
if(!pd) printf("-1\n");
else printf("%d\n",d[n]);
return 0;
}