CyclesAndColorings
先随便求出一棵生成树,去掉生成树的边之后:
如果是二分图,那么原图是两个二分图组合起来的,假设第一个二分图中 i i 的颜色是 ,第二个二分图中 i i 的颜色是 ,那么在总的图中颜色变成 2×color1[i]+color2[i] 2 × c o l o r 1 [ i ] + c o l o r 2 [ i ] 即可(颜色下标从 0 0 开始)。
如果不是二分图,则一定有一个奇环,因为生成树的边没有被删,所以删去后图仍然连通。
时间复杂度 。
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, m, c[N], f[N], depth[N], parent[N];
vector<int> adj[N], tree[N];
bool visit[N];
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
void dfs(int x) {
visit[x] = true;
for (auto y : adj[x]) {
if (!visit[y]) {
depth[y] = depth[x] + 1;
parent[y] = x;
dfs(y);
}
}
}
bool find_odd_circle() {
for (int i = 1; i <= n; ++i) {
visit[i] = false;
}
for (int i = 1; i <= n; ++i) {
if (!visit[i]) {
parent[i] = 0;
depth[i] = 1;
dfs(i);
}
}
for (int x = 1; x <= n; ++x) {
for (auto y : adj[x]) {
if (!(depth[x] + depth[y] & 1)) {
puts("2");
if (depth[x] < depth[y]) {
swap(x, y);
}
printf("%d", depth[x] - depth[y] + 1);
for (int i = x; i != parent[y]; i = parent[i]) {
printf(" %d", i);
}
putchar(10);
return true;
}
}
}
return false;
}
void dfs_color(int x) {
for (auto y : tree[x]) {
if (!~c[y]) {
c[y] = c[x] ^ 1;
dfs_color(y);
}
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int T;
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
c[i] = -1;
f[i] = i;
adj[i].clear();
tree[i].clear();
}
for (int i = 1; i <= m; ++i) {
int x, y;
scanf("%d %d", &x, &y);
if (find(x) != find(y)) {
f[find(x)] = find(y);
tree[x].push_back(y);
tree[y].push_back(x);
} else {
adj[x].push_back(y);
adj[y].push_back(x);
}
}
if (!find_odd_circle()) {
for (int i = 1; i <= n; ++i) {
if (!~c[i]) {
c[i] = 0;
dfs_color(i);
}
}
puts("1");
for (int i = 1; i <= n; ++i) {
printf("%d%c", ((c[i] << 1) + (depth[i] & 1)) + 1, i == n ? '\n' : ' ');
}
}
}
return 0;
}
K Perfect Matchings
首先求出任意一组完美匹配 M M ,通过找增广路,如果发现了一个偶环,那么就找到了一个新的匹配 。
考虑一条在 M M 中但不在 中的边 (u,v) ( u , v ) ,所有匹配可以分成有 (u,v) ( u , v ) 的和没有 (u,v) ( u , v ) 的,分别递归即可。
考虑计算复杂度,不难发现整个搜索过程是一棵完全二叉树的形式(即每个节点都有两个儿子),在每个节点需要花费 O(m) O ( m ) 的时间,而每个非叶节点都对应一个新的匹配,所以节点数是 O(k) O ( k ) 的。
时间复杂度 O(mk) O ( m k ) 。
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int n, m, start, remain, visit_t, visit[N], parent[N], match_l[N], match_r[N];
bool already, stay[N], in_stack[N], erased[N][N];
vector<int> adj_l[N], adj_r[N];
bool find_match(int x) {
if (visit[x] == visit_t) {
return false;
}
visit[x] = visit_t;
for (auto y : adj_l[x]) {
if (!match_r[y] || find_match(match_r[y])) {
match_r[y] = x;
match_l[x] = y;
return true;
}
}
return false;
}
void find_cycle(int x) {
if (already) {
return;
}
visit[x] = visit_t;
in_stack[x] = true;
int y = match_l[x];
for (auto z : adj_r[y]) {
if (already) {
break;
}
if (!stay[z] && !erased[z][y] && z != match_r[y]) {
parent[z] = y;
if (visit[z] != visit_t) {
find_cycle(z);
} else if (in_stack[z]) {
start = z;
already = true;
break;
}
}
}
in_stack[x] = false;
}
void dfs() {
int match[N];
for (int i = 1; i <= n; ++i) {
match[i] = match_l[i];
}
++visit_t;
already = false;
for (int i = 1; i <= n && !already; ++i) {
if (visit[i] != visit_t && !stay[i]) {
find_cycle(i);
}
}
if (!already) {
return;
}
int x = start;
do {
match[x] = parent[x];
x = match_r[parent[x]];
} while (x != start);
stay[x] = true;
dfs();
stay[x] = false;
if (!remain || !--remain) {
return;
}
erased[x][match_l[x]] = true;
for (int i = 1; i <= n; ++i) {
swap(match[i], match_l[i]);
match_r[match_l[i]] = i;
}
dfs();
for (int i = 1; i <= n; ++i) {
swap(match[i], match_l[i]);
match_r[match_l[i]] = i;
}
erased[x][match_l[x]] = false;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d %d %d", &n, &m, &remain);
for (int i = 1; i <= m; ++i) {
int x, y;
scanf("%d %d", &x, &y);
y -= n;
adj_l[x].push_back(y);
adj_r[y].push_back(x);
}
for (int i = 1; i <= n; ++i) {
++visit_t;
if (!find_match(i)) {
puts("No");
return 0;
}
}
if (!remain) {
puts("Yes");
return 0;
}
dfs();
puts(remain ? "No" : "Yes");
return 0;
}
Circular Intervals
考虑二分答案,如果没有环的限制那么贪心确定就行了,否则选择 L1 L 1 正着扫一遍得到最小的 An A n ,再从 An A n 逆推回来最大的 A1 A 1 ,不难发现这就是最优策略。
时间复杂度 O(nlogm) O ( n log m ) 。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool check(const vector<pair<ll, ll>> &intervals, ll limit, ll circle) {
int n = intervals.size();
ll current = intervals[0].first;
for (int i = 1; i < n; ++i) {
if (current + limit > intervals[i].second) {
return false;
}
current = max(intervals[i].first, current + limit);
}
ll right = current;
for (int i = n - 2; ~i; --i) {
current = min(intervals[i].second, current - limit);
}
return circle - right + current >= limit;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
ll m;
scanf("%lld %d", &m, &n);
vector<pair<ll, ll>> intervals(n);
for (int i = 0; i < n; ++i) {
scanf("%lld %lld", &intervals[i].first, &intervals[i].second);
}
ll l = 1, r = m, result = 0;
while (l <= r) {
ll mid = l + r >> 1;
if (check(intervals, mid, m)) {
result = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
printf("%lld\n", result);
return 0;
}