小 K 在 MC 里面建立很多很多的农场,总共 n n n 个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共 m m m 个),以下列三种形式描述:
但是,由于小 K 的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。
第一行包括两个整数 n n n 和 m m m,分别表示农场数目和小 K 记忆中的信息数目。
接下来 m m m 行:
如果存在某种情况与小 K 的记忆吻合,输出 Yes
,否则输出 No
。
3 3
3 1 2
1 1 3 1
2 2 3 2
Yes
对于 100 % 100\% 100% 的数据,保证 1 ≤ n , m , a , b , c ≤ 5 × 1 0 3 1 \le n,m,a,b,c \le 5 \times 10^3 1≤n,m,a,b,c≤5×103。
#include
using namespace std;
#define pii pair<int,int>
#define il inline
#define re register
#define FOR(i,n,s) for(int i=(s);i<=(n);i++)
const int MAXN=1e8+5;
vector<pii> edges[MAXN];
int dis[MAXN];
int n,m,s;
int cnt[MAXN];
bool inQueue[MAXN];
queue<int> q;
//SPFA
il void add(int u,int v,int w)
{
edges[u].emplace_back(v,w);
}
il bool SPFA(int s)
{
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
q.push(s);
inQueue[s]=true;
while(!q.empty())
{
int x=q.front();q.pop();
inQueue[x]=false;
for(auto edge:edges[x])
{
if(dis[edge.first]<=dis[x]+edge.second) continue;
dis[edge.first]=dis[x]+edge.second;
if(!inQueue[edge.first])
{
q.push(edge.first);
inQueue[edge.first]=true;
cnt[edge.first]++;
if(cnt[edge.first]>=n+1) return false;
}
}
}
return true;
}
//标准的SPFA模板
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v,w,opt;
cin>>opt>>u>>v;
if(opt<3) cin>>w;
if(opt==1) add(u,v,-w);
if(opt==2) add(v,u,w);
if(opt==3) {add(u,v,0);add(v,u,0);}
}//建图
for(int i=0;i<=n;i++) add(0,i,0);//建立超级源点
if(!SPFA(0)) cout<<"No";
else cout<<"Yes";
return 0;
}
题目其中 m 条信息有如下三种形式:
1. a i − a j > = c 1.a_i-a_j>=c 1.ai−aj>=c
2. a i − a j < = c 2.a_i-a_j<=c 2.ai−aj<=c
3. a i = a j 3.a_i=a_j 3.ai=aj
我们可以把这三种形式转化为:
1. a j < = a i − c 1.a_j<=a_i-c 1.aj<=ai−c
2. a i < = a j + c 2.a_i<=a_j+c 2.ai<=aj+c
3. a i − a j = 0 3.a_i-a_j=0 3.ai−aj=0
看到这三个不等式我们很容易就会想到这道题用查分约束解决
根据这三个不等式,我们可以依次建边:
1. ( j , i , − c ) 1.(j,i,-c) 1.(j,i,−c)
2. ( i , j , c ) 2.(i,j,c) 2.(i,j,c)
3. ( i , j , 0 ) ( j , i , 0 ) 3.(i,j,0)(j,i,0) 3.(i,j,0)(j,i,0)
然后我们跑一遍SPFA即可(更多关于模板的细节可看前几篇题解)