[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号点))