BZOJ2330: [SCOI2011]糖果 差分约束+spfa

差分约束:把给出的所有约束条件改成形如a-b>=x的形式

x=1:d[a]==d[b],有 d[a]-d[b]>=0, d[b]-d[a]>=0

x=2:d[a]<d[b],有 d[b]-d[a]>=1

x=3:d[a]>=d[b],有 d[a]-d[b]>=0

x=4:d[a]>d[b],有 d[a]-d[b]>=1

x=5:d[a]<=d[b],有 d[b]-d[a]>=0

依照这个建边就可以了。

刚开始把所有点都放进队列里,dis的初始值要都设成1(因为题目中要求每个小朋友都需要有糖果)

如果想加附加源的话要逆着加边,否则超时(我也不知道为啥。。)

要判断一下图是否存在着环(如果一个点被松弛超过n次则存在环)

跑最长路后所有点的dis值之和即为答案。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100005;
int n,m;
long long ans;
int to[N*4],nxt[N*4],w[N*4],lj[N],cnt;
void insert(int f,int t,int p)
{
	to[++cnt]=t;
	nxt[cnt]=lj[f];
	lj[f]=cnt;
	w[cnt]=p;
}
int d[N],q[N*5],cir[N];
bool inq[N];
bool spfa()
{
	for(int i=1;i<=n;i++) q[i]=i,d[i]=1,inq[1]=true,cir[i]=1;
	int h=1,t=n,x,j;
	while(h!=t+1)
	{
		x=q[h];
		inq[x]=false;
		for(int i=lj[x];i;i=nxt[i])
		{
			j=to[i];
			if(d[x]+w[i]>d[j])
			{
				d[j]=d[x]+w[i];
				if(++cir[j]>=n) return false;
				if(!inq[j])
				{
					q[++t]=j;
					if(t==N) t=0;
					inq[j]=true;
				}
			}
		}
		h++;
		if(h==N) h=0;
	}
	return true;
}
int main()
{
	scanf("%d%d",&n,&m);
	int o,a,b;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&o,&a,&b);
		switch(o)
		{
			case 1:insert(a,b,0),insert(b,a,0);break;
			case 2:if(a==b){printf("-1");return 0;}
			insert(a,b,1);break;
			case 3:insert(b,a,0);break;
			case 4:if(a==b){printf("-1");return 0;}
			insert(b,a,1);break;
			case 5:insert(a,b,0);break;
		}
	}
	if(!spfa()) {printf("-1");return 0;}
	for(int i=1;i<=n;i++) ans+=d[i];
	printf("%lld",ans);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值