洛谷 P1993 小K的农场 题解

洛谷 P1993 小K的农场 题解

2020-2-22 xiaoh

题意

nn个点(1n1041\leq n\leq 10^4),每个点都有一个权值wiw_i.接下来给mm个限制条件(1m1041\leq m\leq 10^4),每个限制条件为以下三种之一:
1.wawbcw_a-w_b\geq c
2.wawbcw_a-w_b\leq c
3.wa=wbw_a=w_b
求是否存在满足一组满足条件的ww

题解

一道比较常见的差分约束系统的题.三种情况对应的连边方式为
1:vu1:v\to u,边权为c-c;
2:uv2:u\to v,边权为cc;
3:uv,vu3:u\to v,v\to u,边权为00.
再建一个新的节点,把它和所有节点连上一条边权为00的边即可建图.
但是仔细看数据,发现数据都是10410^4级别的,光用裸的SPFASPFA比较悬,常见的SPFASPFA的优化有很多,比如把堆换成栈来加速判负环的速度等.
当然,在此隆重介绍一种比较玄学的SPFASPFA的优化:SLF(SmallLabelFirst)SLF(Small Label First) 优化.具体思路很简单,由于SPFASPFA算法严格按照FIFOFIFO的规则来,不免有一点粗暴,所以考虑在将一个新节点uu加入队列前,先判断一下它与队首元素headhead的关系,若disu<disheaddis_u<dis_{head},那么将其插入队首;否则插入队尾.这样可以使SPFASPFA的复杂度玄学的快很多(当然要是有毒瘤刻意卡的话也可能慢到BellmanFordBellman-Ford).本题不开SLFSLF优化仅能跑到8080分的好成绩,而且时间是7.39s7.39s,但是一开SLFSLF优化,跑的跟风一样不但能获得100100分的好成绩.
证据在此:
优化前:

优化后:

传说中把2.17s变成7ms的力量就是这个了

Code

#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T &x)
{
	x=0;
	int f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
}
	while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
	x*=f;
	return;
}	
template<typename T>
void write(T x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>=10) write(x/10);
	putchar(x%10+'0');
	return;
}
const int MAXN=10010,MAXM=100010,inf=999999999;
int n,m;
int tot=1;
int edge[MAXM*4],nxt[MAXM*4],w[MAXM*4],hd[MAXN];
inline void add_edge(int u,int v,int c)
{
	edge[tot]=v,w[tot]=c,nxt[tot]=hd[u],hd[u]=tot++;
}
deque<int> q;
bool book[MAXN];
int cnt[MAXN];
int dis[MAXN];
inline bool SPFA()
{
	for(int i=1;i<=n;i++) dis[i]=inf;
	q.push_back(n+1),book[n+1]=1,cnt[n+1]=1;
	while(!q.empty())
	{
		int x=q.front();q.pop_front();
		book[x]=0;
		for(int i=hd[x];i;i=nxt[i])
		if(dis[edge[i]]>dis[x]+w[i])
		{
			dis[edge[i]]=dis[x]+w[i];
			cnt[edge[i]]=cnt[x]+1;
			if(cnt[edge[i]]>n) return false;
			if(!book[edge[i]])
			{
				if(dis[edge[i]]<dis[q.front()]) q.push_front(edge[i]);
				else q.push_back(edge[i]);
				book[edge[i]]=1;
			}
		}
	}
	return true;
}
int main()
{
	read(n),read(m);
	for(int i=1;i<=m;i++)
	{
		int op,u,v,c;
		read(op),read(u),read(v);
		switch(op)
		{
			case 1:read(c),add_edge(u,v,-c);break;
			case 2:read(c),add_edge(v,u,c);break;
			case 3:add_edge(u,v,0),add_edge(v,u,0);break;
		}
	}
	for(int i=1;i<=n;i++) add_edge(n+1,i,0);
	if(SPFA()) printf("Yes\n");
	else printf("No\n");
	return 0;
}