CF1610F 题解
CF1610F 题解
题意: 给一张无向图,边权为1或2,要求给每一条边定向,
使得
其中
在这里提供两种做法。
法一:
定义
首先发现,若存在某个点
故最终答案的上界是
我们再次仔细地观察一下,发现:
若有三个点
那么,我们此时将这两条边删除,并在
这样并不会对答案造成影响,也就是说新图和原图在这个问题上等价,因为:
我们这样做并未对
故答案上界仍然不变。我们称这样的操作为缩边操作。
那我们就不停地进行缩边的操作,直到在图中无法进行任何缩边操作。
那么此时,我们发现,现在的图中每个点至多有两条边相连,且所有出边的权互不相同。
也就是说,现在的图由若干个环和链组成,其中每个环和链的边权都呈
容易发现,任意两个环或链间互不影响,故可以对每条环和链分别考虑。
而对于每一个环或每一条链,我们只需要找出其上面的某一条边,并任意定一个向,
再沿着这条边的方向遍历整个环或链,并将每条边的方向定成其被遍历的方向即可。
具体来说,就是:
如果有一条边
我们就将该边方向定成
以上的定向方法必然最优,因为所有
那么,我们发现,我们给最后这张新图定向后,答案已经等于上界了,
或者说,就是此时定的向使得:
也就是说,我们现在需要考虑的就是,如何从新图的定向方案推出原图的定向方案。
我们注意到,若有一条已定向的边
在原图中,我们就可以将
我们发现,一次次的缩边操作,使边与边之间形成了一个形如二叉树森林的关系,
其中一条边的左右儿子边的含义,是该边由其左右儿子边进行的缩边操作形成。
那么我们只需要建出这个森林,就可以由新图中边的方向,推出原图中边的方向。
容易发现,这样的定向方法,使得原图的答案达到了上界,故这样做是足够优秀的。
至于时间复杂度,可以做到
法二:
和法一相同之处在于,这个方法的目标,也是让答案达到上界。
我们构造一张点数为
定义
定义
-
对
\forall i,j \in [1,n] ,若w(i,j) = 1 ,则在新图中有一条边(i,j) ; -
对
\forall i,j \in [n+1,2n] ,若w(i,j) = 2 ,则在新图中有一条边(i,j) ; -
对
\forall i \in [1,n] ,有:- 若
g_1(i),g_2(i) 皆为奇数,则在新图中有一条边(i,i+n) ; - 若只有
g_1(i) 为奇数,则在新图中有一条边(i, 2n + 1) ; - 若只有
g_2(i) 为奇数,则在新图中有一条边(i + n, 2n + 1) .
- 若
我们发现,这样的图中一定存在一条欧拉回路,因为所有点的度数都是偶数。
那我们在图中找出任意一条欧拉回路,并沿着遍历方向给每条边定向,
最后再将原图的边与新图中上方
我们发现,这样定向使得每个
至于原因,请读者自行思考。
这样的复杂度是线性的,优于第一种做法,且很好写。
下面给出法二的代码:(如果想看法一代码,可以看CF官解)
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define SZ(x) (int(x.size()))
#define pii pair < int , int >
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
const int N (6e5 + 10);
vector < pii > G[N];
int n, m, ans, flg[N], res[N];
void add (int u, int v, int i) {
G[u].pb(mp(v, i)), G[v].pb(mp(u, i));
}
void dfs (int u) {
while (!G[u].empty()) {
pii e = G[u].back(); G[u].pop_back();
if (res[e.se] < 0) res[e.se] = (u < e.fi), dfs(e.fi);
}
}
int main () {
n = read(), m = read();
rep (i, 1, m) {
int u = read(), v = read(), w = read(); flg[i] = (u < v);
if (w == 1) add (u, v, i); else add (u + n, v + n, i);
}
rep (i, 1, n) ans += (SZ(G[i]) & 1);
cout << ans << endl;
rep (i, 1, n) {
if ((SZ(G[i]) & 1) && (SZ(G[i + n]) & 1)) add (i, i + n, i + m);
else {
if (SZ(G[i]) & 1) add (i, n * 2 + 1, i + m);
if (SZ(G[i + n]) & 1) add (i + n, n * 2 + 1, i + m);
}
}
memset (res, -1, sizeof(res)), rep (i, 1, n * 2 + 1) dfs (i);
rep (i, 1, m) putchar (2 - (flg[i] != res[i]) + '0');
return 0;
}