2018年暑假第四次周赛-图论部分题解
2018年暑假第四次周赛-图论部分题解
A。信我啊,这是签到题
这题是签到题
数据出锅,多组后来补的,后来直接删除了一整个文本。纯属意外。
出签到题难啊,本意是一道题教会17一个模型的。说没学过不该出的是没好好看题目的第四行。
你发现当图所有的点和与这个点直接相连(由边直接连接)的点的个数为偶数的时候,你被锤的概率降低了一点点点点点
这句话就是无向图欧拉回路判断定理。不懂百度,解法都丢题目上了。所以只要判断是否所有点的度是偶数,在并查集维护联通即可。
几个定理自觉记一下笔记。图是否联通是欧拉回路必备的坑点。
下面几句话自觉记笔记。
如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。
- 无向图存在欧拉回路的充要条件是,所有点的度是偶数(且图联通)。
- 有向图存在欧拉回路的充要条件是,所有点的入度等于出度(且图联通)。
回路是一个点出发也要回到那个点,通路不需要回到起点。同样都要经过所有边。
- 无向图存在欧拉通路的充要条件是,所有点的度是偶数。允许例外,两个点度为奇数。这两个点分别是起点和终点(且图联通)。
- 有向图存在欧拉通路的充要条件是,所有点的入度等于出度。允许例外一个点入度比出度大一,另一个点入度比出度小一(且图联通)。
证明自己百度或者翻《离散数学》
扩展:欧拉回路可以找路径。算法自己找
#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\n", ans);
}
return 0;
}