【洛谷P3275】糖果【差分约束】【负环】

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P3275
nn个小朋友和mm个要求,其中要求的格式如下:

  • 1 A B1\ A\ B, 表示第 AA 个小朋友分到的糖果必须和第 BB 个小朋友分到的糖果一样多;
  • 2 A B2\ A\ B, 表示第 AA 个小朋友分到的糖果必须少于第 BB 个小朋友分到的糖果;
  • 3 A B3\ A\ B, 表示第 AA 个小朋友分到的糖果必须不少于第 BB 个小朋友分到的糖果;
  • 4 A B4\ A\ B, 表示第 AA 个小朋友分到的糖果必须多于第 BB 个小朋友分到的糖果;
  • 5 A B5\ A\ B, 表示第 AA 个小朋友分到的糖果必须不多于第 BB 个小朋友分到的糖果;

求至少要多少个糖果使得每个小朋友都可以分到糖果并且所有要求都满足。


思路:

这道题的查封约束十分的明显。可以直接列出来:

  1. 需要满足x=yx=y,为了写成aba\leq b的形式,可以改成xyx\leq yyxy\leq x
  2. 需要满足x<yx< y,也就是说ay1a\leq y-1
  3. 需要满足xyx\geq y,直接反过来变成yxy\leq x即可
  4. 需要满足x>yx>y,即xy+1x\geq y+1,移项得yx1y\leq x-1
  5. 需要满足xyx\leq y,不解释了

对于连边方式2和4需要特判,如果x=yx=y那么明显是不成立的。
但是这道题很奇怪,负权+跑最短路+判负环只能拿70分。改成正权+最长路+判正环才可以。
而且第六个点明显是卡了spfaspfa的,add(0,i,0)(i=1n)add(0,i,0)(i=1\sim n)会被卡T。其实应该是可以随机连边的,应该可以过掉,但是看到说add(0,i,0)(i=n1)add(0,i,0)(i=n\sim1)就可以过就没有随机了。
总之一道神题orz就对了。


代码:

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;

const int N=100010;
const int M=300010;
int n,m,tot,x,y,z;
int head[N],cnt[N];
ll dis[N],ans,minn;
bool vis[N];

struct edge
{
	int to,next;
	ll dis;
}e[M];

void add(int from,int to,ll dis)
{
	e[++tot].to=to;
	e[tot].dis=dis;
	e[tot].next=head[from];
	head[from]=tot;
}

bool spfa()
{
	memset(dis,0xcf,sizeof(dis));
	memset(vis,0,sizeof(vis));
	queue<int> q;
	q.push(0);
	vis[0]=1;
	dis[0]=0;
	cnt[0]=1;
	while (q.size())
	{
		int u=q.front(),v;
		q.pop();
		vis[u]=0;
		for (int i=head[u];~i;i=e[i].next)
		{
			v=e[i].to;
			if (dis[v]<dis[u]+e[i].dis)  //最长路
			{
				dis[v]=dis[u]+e[i].dis;
				cnt[v]=cnt[u]+1;
				if (cnt[v]>n+1) return 0;
				if (!vis[v])
				{
					q.push(v);
					vis[v]=1;
				}
			}
		}
	}
	return 1;
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&z,&x,&y);
		if (z%2==0&&x==y) return !printf("-1");  //特判,我大于我自己
		if (z==1) add(x,y,0),add(y,x,0);
		if (z==2) add(x,y,1);
		if (z==3) add(y,x,0);
		if (z==4) add(y,x,1);
		if (z==5) add(x,y,0);
	}
	for (int i=n;i>=1;i--)
		add(0,i,0);
	minn=2147483647;
	if (spfa())
	{
		for (int i=1;i<=n;i++)
		{
			minn=min(minn,dis[i]);
			ans+=dis[i];
		}
		printf("%lld\n",ans-(minn-1)*(ll)n);
	}
	else printf("-1");
	return 0;
}
posted @ 2019-02-16 21:25  全OI最菜  阅读(95)  评论(0编辑  收藏  举报