[BZOJ3436]小K的农场

题目描述 Description###

\(K\)\(MC\) 里面建立很多很多的农场,总共\(n\) 个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共\(m\) 个),以下列三种形式描述:农场\(a\) 比农场\(b\) 至少多种植了\(c\) 个单位的作物,农场\(a\) 比农场\(b\) 至多多种植了\(c\) 个单位的作物,农场\(a\) 与农场\(b\) 种植的作物数一样多。但是,由于小\(K\) 的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。

输入描述 Input Description###

第一行包括两个整数\(n\)\(m\) ,分别表示农场数目和小\(K\) 记忆中的信息数目。
接下来m行:
如果每行的第一个数是1,接下来有3个整数\(a,b,c\) ,表示农场\(a\) 比农场\(b\) 至少多种植了\(c\) 个单位的作物。
如果每行的第一个数是2,接下来有3个整数\(a,b,c\) ,表示农场\(a\) 比农场\(b\) 至多多种植了\(c\) 个单位的作物。
如果每行第一个数是3,家下来有2个整数\(a,b\) ,表示农场\(a\) 终止的数量和\(b\) 一样多。

输出描述 Output Description###

如果存在某种情况与小K的记忆吻合,输出“Yes”,否则输出“No”。

样例输入 Sample Input###

3 3
3 1 2
1 1 3 1
2 2 3 2

样例输出 Sample Output###

Yes

数据范围及提示 Data Size & Hint###

三个农场种植数量可以为(2,2,1)。
对于100%的数据 \(1<=n,m,a,b,c<=10000\) .

之前的一些废话###

停课第二天,继续努力!

题解###

查分约束系统裸题。我们考虑一个图中的一条边\(Edge(u,v,w)\) ,\(dis_u\) 表示源点到u的最短路,满足这个性质:\(dis_u + w\geq dis_v\) ,所以我们可以考虑用这个性质建图。本题中的性质1是这样的:\(dis_a\geq dis_b+c\) ,我们通过移项变号一系列的操作,变成了:\(dis_a-c\geq dis_b\) ,即a向b连一条-c的边。对于性质2:\(dis_a\leq dis_b+2\) ,即b向a连一条c的边,对于性质3,我们直接在a与b之间连一条双向权值为0的边即可。然后我们来思考何时满足题意,当有一组解满足题意时,即所有点的\(dis\) 都确定了,当无解的时候,所有的\(dis\) 都无法确定,很显然就是判一个图中是否带有负环。

代码###

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define mem(a,b) memset(a,b,sizeof(a))
inline int read()
{
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
	return x*f;
}
const int maxn=10010;
struct Edge
{
	int u,v,w,next;
	Edge() {}
	Edge(int _1,int _2,int _3,int _4):u(_1),v(_2),w(_3),next(_4) {}
}e[maxn<<1];
int n,m,Q1[maxn],a[maxn],b[maxn],c[maxn],ce=-1,first[maxn],dis[maxn],cnt[maxn];
bool vis[maxn],VIS[maxn];
queue<int> Q;
void addEdge(int a,int b,int c){e[++ce]=Edge(a,b,c,first[a]);first[a]=ce;}
bool SPFA()
{
	mem(dis,42);
	Q.push(0);vis[0]=1;dis[0]=0;cnt[0]=1;
	while(Q.size())
	{
		int now=Q.front();Q.pop();
		for(int i=first[now];i!=-1;i=e[i].next)
		    if(dis[now]+e[i].w<dis[e[i].v])
		    {
		    	dis[e[i].v]=dis[now]+e[i].w;
		    	if(!vis[e[i].v])
				{
					Q.push(e[i].v);
					vis[e[i].v]=1;
					cnt[e[i].v]++;
					if(cnt[e[i].v]>n+1)return 0;
				} 
			}
		vis[now]=0;
	}
	return 1;
}
int main()
{ 
	mem(first,-1);
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		Q1[i]=read();a[i]=read(),b[i]=read();
		if(Q1[i]==1)c[i]=read(),addEdge(a[i],b[i],-c[i]);
		else if(Q1[i]==2)c[i]=read(),addEdge(b[i],a[i],c[i]);
		else if(Q1[i]==3)addEdge(a[i],b[i],0),addEdge(b[i],a[i],0); 
	}
	for(int i=1;i<=n;i++)addEdge(0,i,0); 
	if(SPFA())printf("Yes\n");
	else printf("No\n");
	return 0;
}

总结###

md调了好长时间发现没有输入c...以后判一个图是否带有负环最好创建一个0号节点向所有的节点连一条权值为0的边,保证整个图联通,好判负环。注意判负环的条件是\(cnt[now]>n\) (n为节点总数(包括0号点))

posted @ 2017-10-24 19:43  小飞淙的云端  阅读(199)  评论(0编辑  收藏  举报