JZOJ3233.照片

9 篇文章 0 订阅
3 篇文章 0 订阅
题目描述
  • 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 n2105

错解——并查集
  • 这鬼算法耗了我一个下午的脑力,结果发现这竟然是错误的!
  • 这道题看起来与奇偶游戏似乎有相似的妙处,但其实这两者有本质的区别。具体就不说了,感性理解一下。
半正解——差分约束
  • 这算法对我来说很陌生,好像也就打过那么一两次。
  • 它其实就是利用“数形结合的思想”,把几个数之间的不等关系转换成一个图。
  • 如果 b − a &lt; = c b-a&lt;=c ba<=c,则从 a a a b b b连一条权值为 c c c的边即可。
  • 这道题有三个不等关系,我们设 q z [ i ] qz[i] qz[i]代表前 i i i个位置斑点牛的个数。则每个区间 [ l , r ] [l,r] [l,r]代表 q z [ r ] − q z [ l − 1 ] &lt; = 1 qz[r]-qz[l-1]&lt;=1 qz[r]qz[l1]<=1
  • 且对于任意 i 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 qz[i+1]qz[i]<=1,qz[i]qz[i+1]<=0
  • 答案即使从 0 0 0 n n n的最短路。
  • 但这道题特别坑,会卡SPFA。
  • 所以要加一些鬼畜优化。
正解——DP
  • 这方法没什么好说的,就是找出如果这个位置是斑点牛,前一个斑点牛的范围 [ l i , r i ] [l_i,r_i] [li,ri],然后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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值