JZOJ3233.照片

题目描述

  • 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。

数据范围

n 2 1 0 5 n \le 2*10^5

错解——并查集

  • 这鬼算法耗了我一个下午的脑力,结果发现这竟然是错误的!
  • 这道题看起来与奇偶游戏似乎有相似的妙处,但其实这两者有本质的区别。具体就不说了,感性理解一下。

半正解——差分约束

  • 这算法对我来说很陌生,好像也就打过那么一两次。
  • 它其实就是利用“数形结合的思想”,把几个数之间的不等关系转换成一个图。
  • 如果 b a &lt; = c b-a&lt;=c ,则从 a a b b 连一条权值为 c c 的边即可。
  • 这道题有三个不等关系,我们设 q z [ i ] qz[i] 代表前 i i 个位置斑点牛的个数。则每个区间 [ l , r ] [l,r] 代表 q z [ r ] q z [ l 1 ] &lt; = 1 qz[r]-qz[l-1]&lt;=1
  • 且对于任意 i i q z [ i + 1 ] q z [ i ] &lt; = 1 , q z [ i ] q z [ i + 1 ] &lt; = 0 qz[i+1]-qz[i]&lt;=1,qz[i]-qz[i+1]&lt;=0
  • 答案即使从 0 0 n n 的最短路。
  • 但这道题特别坑,会卡SPFA。
  • 所以要加一些鬼畜优化。

正解——DP

  • 这方法没什么好说的,就是找出如果这个位置是斑点牛,前一个斑点牛的范围 [ l i , r i ] [l_i,r_i] ,然后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;
}
发布了58 篇原创文章 · 获赞 12 · 访问量 8555

猜你喜欢

转载自blog.csdn.net/fengqiyuka/article/details/95653780
今日推荐