感觉智商还是不太够
正常人的做法:答案上界是显然的,就是 ∑ w \sum w ∑w为奇数的点的个数,因为限制很松。但是直接构造比较棘手,考虑两条边 ( x , v ) (x,v) (x,v)和 ( v , y ) (v,y) (v,y),如果这两条边的权值也相同,那么可以在原图中将 ( x , v ) (x,v) (x,v)和 ( v , y ) (v,y) (v,y)删掉,然后加入 ( x , y ) (x,y) (x,y)这条边。这样每次操作后,边的数目会减少 1 1 1。一直这样操作下去,最后每个点的度数不会超过 2 2 2,因此是若干环和链,显然从链的端点或者环上任意一点开始定向即可。
具体实现方式是:枚举 x x x以及出边 v v v,然后再找到 v v v的一条出边 y y y,将 ( x , v ) (x,v) (x,v)和 ( v , y ) (v,y) (v,y)删掉,加入边 ( x , y ) (x,y) (x,y),然后 v v v赋值成 y y y,继续寻找 y y y的出边。写代码的时候要先把 ( x , v ) (x,v) (x,v)这条边删掉,直到 x x x的所有出边都扩展完了过后再加回来。最后还原方案只需建立二叉树然后从根节点开始遍历即可(和 kruskal \text{kruskal} kruskal重构树比较类似)。
复杂度 O ( n + m ) O(n+m) O(n+m)。但是比较难写,估计要调半天。
聪明人的做法:考虑欧拉回路。本质上就是利用了回路上出边和入边数目相同来构造,但是这道题的建图方式比较难。
考虑建立 2 n + 1 2n+1 2n+1个点(因为 1 1 1和 2 2 2其实本质上是对称的,所以把点复制一遍),建边方式如下:
1.1 1.1 1.1 对于边权为 1 1 1的边 ( u , v ) (u,v) (u,v),加入边 ( u , v ) (u,v) (u,v)
1.2 1.2 1.2 对于边权为 2 2 2的边 ( u , v ) (u,v) (u,v),加入边 ( u + n , v + n ) (u+n,v+n) (u+n,v+n)
1.3 1.3 1.3 设 d i ( j ) d_i(j) di(j)表示 j j j连了多少条度为 i i i的边,如果 d 1 ( u ) d_1(u) d1(u)和 d 2 ( u ) d_2(u) d2(u)都为奇数,加入边 ( u , u + n ) (u,u+n) (u,u+n);如果只有 d 1 ( u ) d_1(u) d1(u)为奇数,加入边 ( u , 2 n + 1 ) (u,2n+1) (u,2n+1);如果只有 d 2 ( u ) d_2(u) d2(u)为奇数,加入边 ( u + n , 2 n + 1 ) (u+n,2n+1) (u+n,2n+1)
跑欧拉回路,然后根据前两类边的方向就可以确定原图中边的方向。
代码很简单。但是确实比较难想到
我是小丑,所以写了第一种做法。
#include
#define ll long long
#define fi first
#define se second
#define pb push_back
using namespace std;
const int N=6e5+5;
int n,m,ok[N],vs[N],vs2[N],U[N],V[N],W[N],du[N],du2[N],cnt,tot;
pair<int,int>ans[N];
vector<int>s[N][2];
vector<int>vec;
vector<int>G[N];
vector<int>G2[N];
int get(int x,int y){
if(y==U[x])return V[x];
return U[x];
}
void dfs(int i,int pre,int j){
while(s[i][j].size()&&vs[s[i][j].back()]){
s[i][j].pop_back();
}if(s[i][j].size()==0){
vec.pb(pre);
return;
}int t=s[i][j].back();
vs[t]=1,s[i][j].pop_back();
cnt++,U[cnt]=get(pre,i),V[cnt]=get(t,i),vs[cnt]=1;
G[cnt].pb(t),G[cnt].pb(pre),du2[t]++,du2[pre]++;
dfs(V[cnt],cnt,j);
}
void dfs2(int u){
while(G2[u].size()&&vs2[G2[u].back()])G2[u].pop_back();
if(G2[u].size()==0)return;
int t=G2[u].back();G2[u].pop_back();
vs2[t]=1,ans[t]={u,get(t,u)},dfs2(get(t,u));
}
void dfs3(int u){
if(G[u].size()){
int l=G[u][0],r=G[u][1];
if(V[l]==U[r]){
ans[l]={U[l],V[l]},ans[r]={U[r],V[r]};
}
else if(U[l]==U[r]){
ans[l]={V[l],U[l]},ans[r]={U[r],V[r]};
}
else if(V[l]==V[r]){
ans[l]={U[l],V[l]},ans[r]={V[r],U[r]};
}
else{
ans[l]={V[l],U[l]},ans[r]={V[r],U[r]};
}if(ans[l].fi!=ans[u].fi){
swap(ans[l].fi,ans[l].se);
swap(ans[r].fi,ans[r].se);
}
dfs3(l),dfs3(r);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m,cnt=m;
for(int i=1;i<=m;i++){
int u,v,w;cin>>u>>v>>w,w--;
s[u][w].pb(i),s[v][w].pb(i);
if(w==0)ok[u]^=1,ok[v]^=1;
U[i]=u,V[i]=v,W[i]=w;
}
for(int i=1;i<=n;i++){
for(int j=0;j<2;j++){
while(1){
while(s[i][j].size()&&vs[s[i][j].back()])s[i][j].pop_back();
if(s[i][j].size()==0)break;
int t=s[i][j].back();s[i][j].pop_back();
vs[t]=1,dfs(get(t,i),t,j);
}while(vec.size()){
int t=vec.back();vec.pop_back();
vs[t]=0,s[U[t]][j].pb(t),s[V[t]][j].pb(t);
}
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<2;j++){
while(1){
while(s[i][j].size()&&vs[s[i][j].back()])s[i][j].pop_back();
if(s[i][j].size()==0)break;
int t=s[i][j].back();s[i][j].pop_back();
vs[t]=1;int k=get(t,i);
du[i]++,du[k]++,G2[i].pb(t),G2[k].pb(t);
}
}
}
for(int i=1;i<=n;i++)if(du[i]==1)dfs2(i);
for(int i=1;i<=n;i++)dfs2(i);
for(int i=1;i<=cnt;i++)if(du2[i]==0)dfs3(i);
for(int i=1;i<=n;i++)tot+=ok[i];
cout<<tot<<"\n";
for(int i=1;i<=m;i++){
if(ans[i].fi==U[i])cout<<1;
else cout<<2;
}
}