1. 程式人生 > 實用技巧 >Luogu P3275 [SCOI2011]糖果

Luogu P3275 [SCOI2011]糖果

思路

這個題是近似於差分約束的模板題(稍微難一點點),差分約束我之前好像聽yt神仙講過。

不懂差分約束的自行百度。

這個題需要注意的就是在建立超級原點的時候要倒敘建邊(理論上正倒序都可以,但是這個題正序過不了應該是資料造得比較奇特)。

update:2020.7.26 正序這個題是可以正常過的,個人認為能不能過取決於程式碼效率

然後再就是套個差分約束板子就行,注意有負環時無解,而且每個點至少有一個糖果。

//這個題也可以用tarjan縮點+toposort做(題解裡看到的),但是相較於差分約束那玩意難寫得多,所以就沒寫那種做法QWQ。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
#define MAXN 100010
typedef long long ll;
int n, k;
int head[MAXN], cnt;
int dis[MAXN], used[MAXN];
bool flag[MAXN];
struct node{
    int nxt, to;
    int val;
} edge[MAXN << 1];
inline int read(void){
    int f = 1, x = 0;char ch;
    do{ch = getchar();if(ch=='-')f = -1;} while (ch < '0' || ch > '9');
    do{ x = (x << 1) +(x << 3) + ch - '0';ch = getchar();} while (ch >= '0' && ch <= '9');
    return f * x;
}
inline void add_edge(int x,int y,int z){
    ++cnt;
    edge[cnt].nxt = head[x];
    edge[cnt].to = y;
    edge[cnt].val = z;
    head[x] = cnt;
    return;
}
void _init(void){
    for (int i = 0; i <= n;++i)
        dis[i] = -INF, flag[i] = 0, used[i] = 1;
    return;
}
ll SPFA(void){
    std::queue<int> Q;
    Q.push(0), flag[0] = 1;
    dis[0] = 0;
    while(!Q.empty()){
        int u = Q.front();
        Q.pop(), flag[u] = 0;
        if(used[u]>=n) return -1;
        for (int i = head[u]; i;i=edge[i].nxt){
            int v = edge[i].to;
            if(dis[u]+edge[i].val>dis[v]){
                dis[v] = dis[u] + edge[i].val;
                ++used[u];
                if(!flag[v])
                    Q.push(v), flag[v] = 1;
            }
        }
    }
    ll res = 0;
    for (int i = 1; i <= n;++i)
        res += dis[i];
    return res;
}
int main(){
    n=read(),k=read();
    for (int i = 1; i <= k;++i){
        int x = read();
        int a = read(), b = read();
        if(!(x&1)&&a==b){puts("-1");return 0;}
        if(x==1) add_edge(a, b, 0), add_edge(b, a, 0);
        if(x==2) add_edge(a, b, 1);
        if(x==3) add_edge(b, a, 0);
        if(x==4) add_edge(b, a, 1);
        if(x==5) add_edge(a, b, 0);
    }
    for (int i = n; i >= 1;--i)
        add_edge(0, i, 1);
    _init();
    printf("%lld\n", SPFA());
    return 0;
}