洛谷P3275 [SCOI2011]糖果 题解
题目链接:P3275 [SCOI2011]糖果
题意:
幼儿园里有 \(N\) 个小朋友,cxy 老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,cxy 需要满足小朋友们的 \(K\) 个要求。幼儿园的糖果总是有限的,cxy 想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
输入格式:
输入的第一行是两个整数 \(N,K\)。接下来 \(K\) 行,表示这些点需要满足的关系,每行 \(3\) 个数字,\(X,A,B\) 。
- 如果 \(X=1\), 表示第 \(A\) 个小朋友分到的糖果必须和第 \(B\) 个小朋友分到的糖果一样多;
- 如果 \(X=2\), 表示第 \(A\) 个小朋友分到的糖果必须少于第 \(B\) 个小朋友分到的糖果;
- 如果 \(X=3\), 表示第 \(A\) 个小朋友分到的糖果必须不少于第 \(B\) 个小朋友分到的糖果;
- 如果 \(X=4\), 表示第 \(A\) 个小朋友分到的糖果必须多于第 \(B\) 个小朋友分到的糖果;
- 如果 \(X=5\), 表示第 \(A\) 个小朋友分到的糖果必须不多于第 \(B\) 个小朋友分到的糖果;
输出格式:
输出一行,表示 cxy 老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 \(-1\)。
数据范围:
\(1 \le N,K \le 10^5,~1\le X\le5, ~1\le A, B\le N\) 。
差分约束神题。
考虑先只添加 \(1,3,5\) 种边,优先处理可以等于的情况。
如果 \(a \le b\) ,建边 \(u \to v\) ,反之若 \(a \ge b\) ,就建边 \(v \to u\) 。
若 \(a=b\) ,也就是第 \(1\) 种边,我们建边 \(u \to v,~v \to u\) 。
不难发现此时所有的强连通分量中的点一定会取相等的值。
然后我们跑一遍 \(\mathtt{Tarjan}\) 缩点,重新建图,顺便加上 \(2,4\) 种边。
注意此时原先的边应该边权为 \(0\) ,新加的边边权应为 \(1\) 。
此时的图一定是个 DAG ,否则一定无解。然后跑个 \(\mathtt{Toposort}\) 算一下就好了
时间复杂度 \(\mathcal{O}(n + m)\)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
namespace FastIO
{
#define gc() readchar()
#define pc(a) putchar(a)
#define SIZ ((int)(1e6))
char buf1[SIZ+5],*_p1,*_p2;
char readchar()
{
if(_p1 == _p2) _p1=buf1,_p2=buf1+fread(buf1,1,SIZ,stdin);
return _p1 == _p2 ? EOF : *_p1++;
}
template<typename T> void read(T &k)
{
char ch = gc(); T x = 0, f = 1;
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = gc(); }
while(isdigit(ch)) { x = (x << 1) + (x << 3) + (ch ^ 48); ch = gc(); }
k = x * f;
}
template<typename T> void write(T k)
{
if(k < 0) { k = -k; pc('-'); }
static T _stk[66]; T _top = 0;
do{ _stk[_top++] = k % 10; k/=10; }while(k);
while(_top){ pc(_stk[--_top] + '0'); }
}
}using namespace FastIO;
#define N ((int)(1e5+15))
bitset<N> ins,vis;
int n,m,k,res,pos=1,stktop,scnt,head[N],f[N];
int sz[N],dfn[N],low[N],stk[N],scc[N],top[N],in[N];
struct query { int op,a,b; } qry[N];
struct Edge{ int u,v,w,next; } e[N * 2];
void addEdge(int u,int v,int w)
{ e[++pos] = {u,v,w,head[u]}; head[u] = pos; }
void Tarjan(int u)
{
static int tim = 0; dfn[u] = low[u] = ++tim;
ins[stk[++stktop] = u] = 1;
for(int i=head[u]; i; i=e[i].next)
{
int v = e[i].v;
if(!dfn[v])
{
Tarjan(v); down(low[u], low[v]);
} else if(ins[v]) down(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
top[++scnt] = u;
for(int p=0; p != u; )
{
p = stk[stktop--]; ins[p] = 0;
scc[p] = scnt; ++sz[scnt];
}
}
}
queue<int> q;
void Toposort()
{
for(int i=1; i<=scnt; i++) if(!in[i]) { q.push(i); f[i] = vis[i] = 1; }
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i=head[u]; i; i=e[i].next)
{
int v = e[i].v, w = e[i].w;
up(f[v], f[u] + w); vis[v] = 1;
if(!(--in[v])) q.push(v);
}
}
for(int i=1; i<=scnt; i++) if(!vis[i]) return cout << "-1\n",void(0);
int res = 0;
for(int i=1; i<=scnt; i++) res += f[i] * sz[i];
cout << res << '\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
read(n); read(m);
for(int i=1,op,a,b; i<=m; i++)
{
read(op); read(a); read(b);
if(op & 1)
{
if(op == 5 || op == 1) addEdge(a,b,0); // a <= b
if(op == 3 || op == 1) addEdge(b,a,0); // b <= a
}else qry[++k] = {op, a, b};
}
for(int i=1; i<=n; i++) if(!dfn[i]) Tarjan(i);
for(int i=1; i<=n; i++) head[i] = 0;
int tmp = pos; pos = 1;
for(int i=2; i<=tmp; i++)
{
int u = scc[e[i].u], v = scc[e[i].v];
if(u != v) { addEdge(u,v,0); ++in[v]; }
}
for(int i=1; i<=k; i++)
{
int u = scc[qry[i].a], v = scc[qry[i].b];
if(u == v) return cout << "-1\n", 0;
if(qry[i].op == 2) { addEdge(u,v,1); ++in[v]; } // a < b
if(qry[i].op == 4) { addEdge(v,u,1); ++in[u]; } // b > a
}
Toposort();
return 0;
}