2021ICPC Shenyang H.Line Graph Matching (dfs tree cutting edge + simple tree dp)

2021 ICPC Shenyang H.Line Graph Matching (dfs tree edge cutting + tree dp)

Preface: The idea is not difficult to think about, mainly because the topic is too difficult to understand.

The meaning of the title: Simplified is to select two adjacent sides in the original picture for pairing, the contribution generated after pairing is the sum of the weights of the two sides, and the pairing of sides cannot be repeated.

Pre-cheese: dfs tree

answer:

1. First of all, it is relatively easy to know that if there are an even number of edges, all the edges can be selected (you can use the dfs tree to simply prove that if the number of edges back to a node of the dfs tree plus the edge connected to the son If the number is even, then it can be perfectly matched; if it is odd, then use this node to connect to the edge of the father, and then it can be perfectly matched. If the edge to the father is used, then the father node is calculating the edge to the son It is necessary to subtract one at a time, so that recursively, you can know that all even-numbered sides must be selected).

2. Next, let’s discuss the situation of odd-numbered edges. First, let’s think about this problem in a relatively simple way. For a graph with odd-numbered edges, if we remove one edge, it will become a graph with even-numbered edges. It can be classified into the above situation. So you only need to delete an edge with the smallest weight, isn't it all right? But we will find that if the deleted edge is a cutting edge, it will be divided into two connected blocks. If the number of edges of the connected block is odd, then it will definitely cause one edge of this connected block to not match perfectly, so we The deleted edge can only be two kinds of edges [1] non-cut edge (a connected graph with an even number of edges is formed after deletion) [2] the number of connected block edges after the cut edge is deleted is For even-numbered sides, the minimum value of the side weights in the above two cases is minn, and the answer is sum-minn.

the code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct edge{
    
    
    int to;
    int id;
    ll w;
};
ll vis[200009];
vector<edge> g[200009];
int dp[200009];//用来求割边,详情请见博客上方连接
int bian[200009];//bian[i]表示子树中,以i为节点的子树有多少条边(子树节点连到i的祖先的边不算在内)
int dep[200009];
int n,m;
void dfs(int now,int fa){
    
    
    dep[now] = dep[fa] + 1;
    int id;
    for(auto j : g[now]){
    
    
        if(j.to == fa){
    
    
            id = j.id;
            continue;
        }
        if(dep[j.to]){
    
    
            if(dep[j.to] > dep[now]){
    
    
                dp[now]--;
            }else{
    
    
                dp[now]++;
                bian[j.to]++;//回边
            }
        }else{
    
    
            dfs(j.to,now);
            dp[now] += dp[j.to];
            bian[now] += (bian[j.to] + 1);//这个1指的是now连向儿子的边
        }
    }
    if(now != 1&&dp[now] == 0 && (bian[now]) % 2 == 1){
    
    //dp[now] == 0表示这个节点连向父亲的边为割边
        					//bian[now] % 2 == 1表示删掉连向父亲的边之后,其子树形成的连通图边的数量为奇数
        vis[id] = 2000000000;//设为无穷大表示不参与选最小的边的统计
    }
}
int main(){
    
    
    scanf("%d %d",&n,&m);
    ll sum = 0;
    for(int i = 1;i <= m;i++){
    
    
        int u,v;
        ll w;
        scanf("%d %d %lld",&u,&v,&w);
        sum += w;
        vis[i] = w;
        edge p;
        p.id = i;p.w = w;p.to = v;
        g[u].push_back(p);
        p.to = u;
        g[v].push_back(p);
    }
    if(m % 2 == 0){
    
    //偶数直接输出
        printf("%lld",sum);
        return 0;
    }
    dfs(1,0);
    ll minn = 2000000000;
    for(int i = 1;i <= m;i++){
    
    
        minn = min(vis[i],minn);
    }
    printf("%lld",sum-minn);
    return 0;
}

Guess you like

Origin blog.csdn.net/m0_51687577/article/details/126778140