[SCOI2011]糖果

题意

Here

思考

最近才学差分约束,于是做了这道题\(QWQ\)

本题是比较裸的差分约束,题目中由以下五种条件(\(link\)代表连边):

  1. \(A\)\(B\)糖果数一样多 \(\to link(A, B, 0), link(B, A, 0)\)
  2. \(A\)糖果数少于\(B \to (A+1 \leq B) \to link(A, B, 1)\)
  3. \(A\)糖果数大于等于\(B \to link(B, A, 0)\)
  4. \(A\)糖果数大于\(B \to (B+1 \leq A) \to link(B, A, 1)\)
  5. \(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\)

posted @ 2018-11-03 13:36  alecli  阅读(225)  评论(0编辑  收藏  举报