[SCOI2011]糖果
题意
思考
最近才学差分约束,于是做了这道题\(QWQ\)
本题是比较裸的差分约束,题目中由以下五种条件(\(link\)代表连边):
- \(A\)和\(B\)糖果数一样多 \(\to link(A, B, 0), link(B, A, 0)\)
- \(A\)糖果数少于\(B \to (A+1 \leq B) \to link(A, B, 1)\)
- \(A\)糖果数大于等于\(B \to link(B, A, 0)\)
- \(A\)糖果数大于\(B \to (B+1 \leq A) \to link(B, A, 1)\)
- \(A\)糖果数小于等于\(B \to link(A, B, 0)\)
最后 \(0\) 向每一个点连一条权值为 \(1\) 的边代表每一个小朋友至少分到一个糖果,\(spfa\) 跑一边最长路,跑最长路的原因是为了使更多的约束条件被满足,每个点最终的 \(dist\) 数组值代表每个小朋友至少分到的糖果数,如果存在负环则判定无解
代码
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll read(){
ll x=0,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-'0';ch=getchar();}
return x * f;
}
const int M = 200020;
const int N = 100010;
struct node{
ll nxt, to, dis;
}edge[M << 1];
ll head[N], num;
void build(ll from, ll to, ll dis){
edge[++num].nxt = head[from];
edge[num].to = to;
edge[num].dis = dis;
head[from] = num;
}
ll n, k, ans, vis[N], dist[N], tot[N];
void spfa(){
memset(dist, 0, sizeof(dist));
queue<ll> q;
q.push(0); dist[0] = 0;
while(!q.empty()){
ll u = q.front(); q.pop();
if(tot[u] == n-1){
puts("-1"); exit(0);
}
vis[u] = 0; tot[u] ++;
for(ll i=head[u]; i; i=edge[i].nxt){
ll v = edge[i].to;
if(dist[v] < dist[u] + edge[i].dis){
dist[v] = dist[u] + edge[i].dis;
if(vis[v] == 0) vis[v] = 1, q.push(v);
}
}
}
}
int main(){
n = read(), k = read();
for(ll i=1; i<=k; i++){
ll op = read(), u = read(), v = read();
if(op == 1){
build(u, v, 0); build(v, u, 0);
}
if(op == 2){
if(u == v){ puts("-1"); return 0; } build(u, v, 1);
}
if(op == 3){
build(v, u, 0);
}
if(op == 4){
if(u == v){ puts("-1"); return 0; } build(v, u, 1);
}
if(op == 5){
build(u, v, 0);
}
}
for(ll i=n; i>=1; i--) build(0, i, 1);
spfa();
for(ll i=1; i<=n; i++) ans += dist[i];
cout << ans;
return 0;
}
总结
拿到题目后分析数据范围,看是否会爆 \(long long\),\(spfa\)判负环是看某个点是否入队超过 \(n\) 次