2018年暑假第四次周赛-图论部分题解

2018年暑假第四次周赛-图论部分题解

A。信我啊,这是签到题

这题是签到题

数据出锅,多组后来补的,后来直接删除了一整个文本。纯属意外。

出签到题难啊,本意是一道题教会17一个模型的。说没学过不该出的是没好好看题目的第四行。
你发现当图所有的点和与这个点直接相连(由边直接连接)的点的个数为偶数的时候,你被锤的概率降低了一点点点点点
这句话就是无向图欧拉回路判断定理。不懂百度,解法都丢题目上了。所以只要判断是否所有点的度是偶数,在并查集维护联通即可。

几个定理自觉记一下笔记。图是否联通是欧拉回路必备的坑点。


下面几句话自觉记笔记。

如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。

  1. 无向图存在欧拉回路的充要条件是,所有点的度是偶数(且图联通)。
  2. 有向图存在欧拉回路的充要条件是,所有点的入度等于出度(且图联通)。

回路是一个点出发也要回到那个点,通路不需要回到起点。同样都要经过所有边。

  1. 无向图存在欧拉通路的充要条件是,所有点的度是偶数。允许例外,两个点度为奇数。这两个点分别是起点和终点(且图联通)。
  2. 有向图存在欧拉通路的充要条件是,所有点的入度等于出度。允许例外一个点入度比出度大一,另一个点入度比出度小一(且图联通)。

证明自己百度或者翻《离散数学》

扩展:欧拉回路可以找路径。算法自己找



#include <bits/stdc++.h>

using namespace std;
const int MAXN = 1e5 + 10;

int n, m, pre[MAXN], deg[MAXN];

void init() {
    for(int i = 1; i <= n; i++ ) {
        pre[i] = i;
    }
}

int findx(int x) {
    return pre[x] == x ? x : pre[x] = findx(pre[x]);
}

void join(int x, int y) {
    int fx = findx(x), fy = findx(y);
    if(fx != fy) {
        pre[fx] = fy;
    }
}

bool same(int x, int y) {
    return findx(x) == findx(y);
}

int main()
{
//    freopen("2.in", "r", stdin);
//    freopen("2.out", "w", stdout);
    int u, v;
    while(~scanf("%d %d", &n, &m)) {
        init();
        memset(deg, 0, sizeof(deg));
        for(int i = 1; i <= m; i++ ) {
            scanf("%d %d", &u, &v);
            join(u, v);
            deg[u]++;
            deg[v]++;
        }
        bool ok = 1;
        int cnt = 0;
        for(int i = 1; i <= n; i++) {
            if(pre[i] == i) {
                cnt++;
            }
            if(deg[i] % 2 != 0) {
                ok = 0;
                break;
            }
        }
        if(ok && cnt == 1) {
            puts("yes");
        } else {
            puts("no");
        }
    }
    return 0;
}

E。【C_W_L】的预言

表面数论,其实是图论题。

[gcd(a_i,a_j) * gcd(a_i+1, a_j+1)≠1 ]

求一个最大集合满足上式。如果 (a_i) , (a_j) 符合上式。那么我们连一条边,题意就是要我们就要求该图的最大团。(一个图的点集合任意两个点之间都有边叫团)最大团等于它补图的最大独立集。

如果 (a_i), (a_j) 符合上式。那么我们连一条边
那么 (a_i), (a_j) 同奇,同偶的时候必定有边。那么补图一定没边。
所以它的补图是一个二分图。我们求它的最大独立集合即可。

二分图最大独立集,匈牙利,网络流随便来一个。

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 555;
typedef long long LL;

int n, m, first[MAXN], sign;

int links[MAXN], vis[MAXN];

LL a[MAXN];

struct Edge {
    int to, w, next;
} edge[MAXN * MAXN];

void init() {
    memset(first, -1, sizeof(first));
    sign = 0;
}

void add_edge(int u, int v, int w) {
    edge[sign].to = v;
    edge[sign].w = w;
    edge[sign].next = first[u];
    first[u] = sign++;
}

int dfs(int x) {
    for(int i = first[x]; ~i; i = edge[i].next) {
        int to = edge[i].to;
        if(!vis[to]) {
            vis[to] = 1;
            if(!links[to] || dfs(links[to])) {
                links[to] = x;
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    while(~scanf("%d", &n)) {
        init();
        for(int i = 1; i <= n; i++ ) {
            scanf("%lld", &a[i]);
        }
        for(int i = 1; i <= n; i++ ) {
            for(int j = 1; j <= n; j++ ) {
                if(__gcd(a[i], a[j]) == 1 && __gcd(a[i] + 1, a[j] + 1) == 1) {
                    add_edge(i, j, 1);
                }
            }
        }
        int ans = n;
        for(int i = 1; i <= n; i++ ) {
            if(a[i] & 1) {
                memset(vis, 0, sizeof(vis));
                ans -= dfs(i);
            }
        }
        printf("%d
", ans);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/Q1143316492/p/9502783.html