Capitalism
算法
差分约束
观察到 \(a_i\) 最后是确定唯一的 + 我是从差分约束专题来的 ,考虑对于 \(a_i\) 的约束条件进行差分约束。
转化约束条件
观察到,
\[\left\{
\begin{array}{lr}
b = 0, \lvert {a_u - a_v} \rvert = 1, & \\
b = 1, a_u + 1 = a_v &
\end{array}
\right.
\]
\[\Downarrow
\]
\[\left\{
\begin{array}{lr}
a_u, a_v \in \mathbb{Z}, & \\
b = 0, a_u + 1 \geq a_v \text{ 且 } a_v + 1 \geq a_u \text{ 且 } a_u \neq a_v, & \\
b = 1, a_u + 1 \geq a_v \text{ 且 } a_u + 1 \leq a_v &
\end{array}
\right.
\]
都可以作为约束条件。
但是需要解决 \(a_u = a_v\) 的情况。
观察到原图如果不是一个二分图(存在奇环),那么不管对于环上每一边 \((u, v)\) 的权值取 \(-1\) 还是 \(1\) ,必不可能使 \(\sum w = 0\) 。
所以原图一定是二分图。
处理方式
根据二分图性质处理
在没有奇环的情况下,一条边相连的两个点的 \(dis\) 一定奇偶性不同 ( \(w \in \{ 1, -1 \}\) 每次跑一条边必定改变 \(dis\) 奇偶性,只有存在奇环时,才能出现奇偶性不变的情况),是不可能出现 \(a_i=a_j\) 的。
因此可以直接跑一遍即可。
终于搞懂了, 耗时 \(2 \text{ } \rm{hours}\) 。
二分图 trick
令当前考虑的边为 \((u, v)\), 其中 \(u\) 为偶数点,
令 \(a_u = 2b_u, a_v = 2b_v + 1\) ,
即可将 \(\lvert {a_u-a_v}\rvert \leq 1\) 转化为 \(b_v\leq b_u\leq b_v+1\) ,这样差值内的整数就只有 \(0/1\) 了。
这个思路来源于 ningago 的题解 。
跑差分约束
无论如何,最后枚举 \(0\) 势点,跑差分约束即可。
不会的见 差分约束模板 。
最短路应用
具体见 FutaRimeWoawaSete 的题解 。
代码
#include <bits/stdc++.h>
const int MAXM = 2e3 + 20;
const int MAXN = 2e2 + 20;
int n, m;
class Graph_class
{
private:
/*染色法判定二分图*/
int Color[MAXN];
bool dfs(int Now, int Colour)
{
Color[Now] = Colour;
for (int i = head[Now]; ~i; i = Edge[i].next) {
int NowTo = Edge[i].to;
int Next_Colour = 3 - Colour;
if(!Color[NowTo] && !dfs(NowTo, Next_Colour)) {
return false;
}else if(Color[NowTo] && Color[NowTo] != Next_Colour) {
return false;
}
}
return true;
}
public:
struct Edge_Node
{
int to, w;
int next;
} Edge[MAXM << 1];
int Edge_Cnt = 0;
int head[MAXN];
void head_init() { for(int i = 1; i <= n; i++) head[i] = -1; }
void addedge(int u, int v, int w)
{
Edge[++Edge_Cnt].to = v, Edge[Edge_Cnt].w = w, Edge[Edge_Cnt].next = head[u];
head[u] = Edge_Cnt;
}
bool IS_BG()
{
return dfs(1, 1);
}
} Graph;
class Solve_Class
{
private:
std::queue<int> Q;
int Neg[MAXN]; // 判断负环
bool inq[MAXN];
int dis[MAXN];
void init()
{
while(!Q.empty()) Q.pop();
memset(Neg, 0, sizeof(Neg));
memset(inq, false, sizeof(inq));
memset(dis, 0x3f, sizeof(dis));
}
bool spfa(int Start)
{
init();
Q.push(Start), Neg[Start]++, inq[Start] = true, dis[Start] = 0;
while(!Q.empty())
{
int Now = Q.front();
Q.pop();
inq[Now] = false;
for (int i = Graph.head[Now]; ~i; i = Graph.Edge[i].next) {
int NowTo = Graph.Edge[i].to, NowW = Graph.Edge[i].w;
if(dis[NowTo] > dis[Now] + NowW) {
dis[NowTo] = dis[Now] + NowW;
if(!inq[NowTo]) Q.push(NowTo), inq[NowTo] = true, Neg[NowTo]++;
if(Neg[NowTo] > n) return true;
}
}
}
return false;
}
public:
void solve()
{
int Ans_Val = -1;
int Ans_S[MAXN];
for (int i = 1; i <= n; i++) {
/*注意这里只要有负环, 必定无解*/
if(spfa(i)) {
printf("NO");
exit(0);
}
int Now_Val = 0;
for (int i = 1; i <= n; i++)
Now_Val = std::max(Now_Val, dis[i]);
if(Now_Val > Ans_Val) {
Ans_Val = Now_Val;
for (int i = 1; i <= n; i++) {
Ans_S[i] = dis[i];
}
}
}
printf("YES\n");
printf("%d\n", Ans_Val);
for (int i = 1; i <= n; i++)
printf("%d ", Ans_S[i]);
}
} Sol;
int main()
{
scanf("%d %d", &n, &m);
Graph.head_init();
for (int i = 1; i <= m; i++) {
int u, v, type;
scanf("%d %d %d", &u, &v, &type);
if(type == 0) Graph.addedge(u, v, 1), Graph.addedge(v, u, 1);
else Graph.addedge(u, v, 1), Graph.addedge(v, u, -1);
}
/*判断是否合法*/
if(!Graph.IS_BG())
printf("NO");
else
{
Sol.solve();
}
return 0;
}
总结
边权 \(\in \{ 1, -1 \}\) 时,拥有一些特殊的性质。
对于绝对值不等式,考虑变形 trick 。

浙公网安备 33010602011771号