CodeForces 603E Pastoral Oddities
题目大意
最开始给定有
N
N
N个点,没有边的图,现在按顺序依次添加
M
M
M条有权值的边。
每次操作后,从已经添加的边中找一个子集,使得每个点的度数都是奇数,且边权最大的边的权值最小。输出这个最小值。
分析
不难得出总度数之和为偶数。
而我们要求每个点的度数为奇数,只有当点数为偶数时才能够满足。
结论: 含有偶数个点的连通块一定存在一棵生成树使得满足每个点的度数都是奇数。
证明: 先随便搞一棵生成树,自叶子向上逐个判断每个节点能否满足条件。若不满足,我们就把它到父亲的边加进去。
只有根节点无法调整。但根据总度数为偶数,我们可以推出根节点度数一定是奇数。
由上面的结论,我们注意到对于图中的任意一个连通块,只有它的点数是偶数的时候才有解。
现在考虑答案求解。
对于当前边权最大的边,如果它可以不存在于答案集合中,那么就删掉它。否则就考虑下一条边。
若某条边可以不存在于答案中,那么我们删掉它后,要么原来的连通块仍然连通,要么分成两个点数偶数的连通块。
注意到一条边被删后,这条边就无法再加进答案集合了。(加进去后反而会导致无解)
那么我们每次加进一条边,我们就尝试把最大边权删掉。
我们分成两种情况来讨论:
当删掉最大边权后仍然连通,这时它必定位于图中的某个环上。
这种情况就有点像最小生成树算法中的破圈法,写个LCT维护一下就是了。
当删掉最大边权后分成了两个连通块。
我们用一个set维护在生成树上的边,从最大边权开始枚举每条边,查询边的两个端点的子图中点数是否是偶数。这个也可以用上面那个LCT顺带着维护一下。
一些细节:
我们写LCT时把边当成一个点,维护路径上的最大值。这样就可以用LCT做破圈法了。
再维护一下LCT中的虚子树的大小和实际子树的大小,用以判断奇偶性。
注意重载set的比较运算符时必须严格重载。
参考代码
#include <set>
#include <cstdio>
#include <algorithm>
using namespace std
;
const int Maxn
= 100000;
const int Maxm
= 300000;
struct LinkCutTree
{
struct Node
{
int key
, id
;
Node
*mx
;
bool is_node
;
int siz
, siz2
;
Node
*fa
, *ch
[2];
bool rev
;
};
Node pool
[Maxn
+ Maxm
+ 5];
Node
*ncnt
, *NIL
;
void init() {
ncnt
= NIL
= &pool
[0];
NIL
->fa
= NIL
->ch
[0] = NIL
->ch
[1] = NIL
->mx
= NIL
;
NIL
->rev
= false;
NIL
->siz
= NIL
->siz2
= 0;
NIL
->key
= -1, NIL
->is_node
= false;
}
Node
*newnode(int k
, int id
, int typ
) {
Node
*p
= ++ncnt
;
p
->key
= k
, p
->id
= id
, p
->siz
= typ
, p
->siz2
= 0;
p
->is_node
= typ
;
p
->fa
= p
->ch
[0] = p
->ch
[1] = NIL
;
p
->mx
= p
;
return p
;
}
void pushup(Node
*rt
) {
if(rt
->key
> rt
->ch
[0]->mx
->key
&& rt
->key
> rt
->ch
[1]->mx
->key
)
rt
->mx
= rt
;
else if(rt
->ch
[0]->mx
->key
> rt
->ch
[1]->mx
->key
)
rt
->mx
= rt
->ch
[0]->mx
;
else rt
->mx
= rt
->ch
[1]->mx
;
rt
->siz
= rt
->is_node
+ rt
->ch
[0]->siz
+ rt
->ch
[1]->siz
+ rt
->siz2
;
}
void pushdown(Node
*rt
) {
if(rt
->rev
) {
if(rt
->ch
[0] != NIL
) rt
->ch
[0]->rev
^= 1;
if(rt
->ch
[1] != NIL
) rt
->ch
[1]->rev
^= 1;
swap(rt
->ch
[0], rt
->ch
[1]);
rt
->rev
= false;
}
}
bool dir(Node
*rt
) {return rt
->fa
->ch
[1] == rt
;}
bool isroot(Node
*rt
) {
return rt
->fa
== NIL
|| (rt
->fa
->ch
[0] != rt
&& rt
->fa
->ch
[1] != rt
);
}
void setchild(Node
*rt
, Node
*x
, int d
) {
rt
->ch
[d
] = x
;
if(x
!= NIL
) x
->fa
= rt
;
}
void rotate(Node
*x
) {
Node
*y
= x
->fa
;
pushdown(y
), pushdown(x
);
int d
= dir(x
);
if(isroot(y
)) x
->fa
= y
->fa
;
else setchild(y
->fa
, x
, dir(y
));
setchild(y
, x
->ch
[!d
], d
);
setchild(x
, y
, !d
);
pushup(y
);
}
void splay(Node
*x
) {
pushdown(x
);
while(!isroot(x
)) {
Node
*y
= x
->fa
;
if(isroot(y
)) rotate(x
);
else {
if(dir(y
) == dir(x
))
rotate(y
);
else rotate(x
);
rotate(x
);
}
}
pushup(x
);
}
void access(Node
*x
) {
Node
*c
= NIL
;
while(x
!= NIL
) {
splay(x
);
x
->siz2
+= x
->ch
[1]->siz
- c
->siz
;
setchild(x
, c
, 1);
pushup(x
);
c
= x
, x
= x
->fa
;
}
}
void makeroot(Node
*x
) {
access(x
), splay(x
);
x
->rev
^= 1;
}
Node
*findroot(Node
*x
) {
access(x
), splay(x
);
Node
*y
= x
;
while(y
->ch
[0] != NIL
)
y
= y
->ch
[0];
pushup(y
);
return y
;
}
void link(Node
*x
, Node
*y
) {
makeroot(x
), makeroot(y
);
x
->fa
= y
;
y
->siz2
+= x
->siz
;
}
void cut(Node
*x
, Node
*y
) {
makeroot(x
);
access(y
), splay(y
);
y
->ch
[0] = NIL
, x
->fa
= NIL
;
pushup(y
);
}
Node
*lca(Node
*x
, Node
*y
) {
access(x
), splay(x
);
splay(y
);
while(y
->fa
!= NIL
) {
y
= y
->fa
;
splay(y
);
}
return y
;
}
Node
*query(Node
*x
, Node
*y
) {
Node
*z
= lca(x
, y
);
Node
*ret
= z
;
access(x
), splay(z
);
if(z
->ch
[1]->mx
->key
> ret
->key
)
ret
= z
->ch
[1]->mx
;
access(y
), splay(z
);
if(z
->ch
[1]->mx
->key
> ret
->key
)
ret
= z
->ch
[1]->mx
;
return ret
;
}
};
struct Edge
{
int u
, v
, w
, id
;
bool operator < (const Edge
&rhs
) const {
return w
== rhs
.w
? id
< rhs
.id
: w
< rhs
.w
;
}
};
LinkCutTree
::Node
*nd
[Maxn
+ Maxm
+ 5];
LinkCutTree tr
;
set
<Edge
> edges
;
Edge e
[Maxm
+ 5];
int N
, M
;
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
tr
.init();
scanf("%d %d", &N
, &M
);
for(int i
= 1; i
<= N
; i
++)
nd
[i
] = tr
.newnode(-1, -1, 1);
int tot
= N
;
for(int i
= 1; i
<= M
; i
++) {
scanf("%d %d %d", &e
[i
].u
, &e
[i
].v
, &e
[i
].w
);
e
[i
].id
= i
;
edges
.insert(e
[i
]);
nd
[N
+ i
] = tr
.newnode(e
[i
].w
, i
, 0);
if(tr
.findroot(nd
[e
[i
].u
]) == tr
.findroot(nd
[e
[i
].v
])) {
LinkCutTree
::Node
*p
= tr
.query(nd
[e
[i
].u
], nd
[e
[i
].v
]);
tr
.splay(p
);
if(p
->key
> e
[i
].w
) {
edges
.erase(e
[p
->id
]);
tr
.cut(p
, nd
[e
[p
->id
].u
]), tr
.cut(p
, nd
[e
[p
->id
].v
]);
tr
.link(nd
[e
[i
].u
], nd
[N
+ i
]), tr
.link(nd
[e
[i
].v
], nd
[N
+ i
]);
} else edges
.erase(e
[i
]);
} else {
tr
.makeroot(nd
[e
[i
].u
]), tr
.makeroot(nd
[e
[i
].v
]);
if(nd
[e
[i
].u
]->siz
% 2 == 1 && nd
[e
[i
].v
]->siz
% 2 == 1)
tot
-= 2;
tr
.link(nd
[e
[i
].u
], nd
[N
+ i
]), tr
.link(nd
[e
[i
].v
], nd
[N
+ i
]);
}
if(tot
== 0) {
set
<Edge
>::iterator it
= edges
.end();
it
--;
while(true) {
LinkCutTree
::Node
*p
= nd
[N
+ it
->id
];
LinkCutTree
::Node
*q
= nd
[e
[it
->id
].u
], *r
= nd
[e
[it
->id
].v
];
tr
.makeroot(p
), tr
.access(q
), tr
.access(r
);
if(q
->siz
& 1) break;
tr
.access(q
);
if(r
->siz
& 1) break;
edges
.erase(it
);
it
= edges
.end();
it
--;
}
it
= edges
.end();
it
--;
printf("%d\n", it
->w
);
} else puts("-1");
}
return 0;
}