【学习笔记】CF1610F Mashtali: a Space Oddysey

感觉智商还是不太够😅

正常人的做法:答案上界是显然的,就是 ∑ 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<bits/stdc++.h>
#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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值