Luogu P3275 [SCOI2011]糖果
阿新 • • 發佈:2020-07-26
思路
這個題是近似於差分約束的模板題(稍微難一點點),差分約束我之前好像聽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; }