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

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

A。信我啊,这是签到题

这题是签到题html

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

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

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


下面几句话自觉记笔记。网络

若是图G中的一个路径包括每一个边刚好一次,则该路径称为欧拉路径(Euler path)。post

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

回路是一个点出发也要回到那个点,通路不须要回到起点。一样都要通过全部边。spa

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

证实本身百度或者翻《离散数学》code

扩展:欧拉回路能够找路径。算法本身找htm


 #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; }#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】的预言

表面数论,实际上是图论题。blog

\[ 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\n", ans); } return 0; }#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\n", ans); } return 0; }

转载于:https://www.cnblogs.com/Q1143316492/p/9502783.html