[BZOJ-1854]游戏

Description

给你n样物品,每个物品可以有两个属性a,b。
你要给每个物品挑选一个属性,使得跳出的属性之组成的数列从1开始连续的数字个数最多。

Solution

这是一道[SCOI2010]的题。
因为每个物品需要2选1,所以我们想到用二分图来做。
物品i有属性a,b,那我们就给Xa -> Yi, Xb -> Yi连边。
然后我们枚举连续属性的最大值,给这个点找增广路,如果找不到就说明这个属性无法到达了。

这道题还有一个很妙的并查集做法。
如果一个物品有属性a,b,那么我们给a -> b连一条边。
然后我们发现对于每个大小为p的联通块,如果没有环,那么可以选p-1个(不选最大的)
如果一个大小为p的联通块,含有环,那么我们这p个都可以选。
所以我们每次用并查集合并两个联通块,还要记录一个vis值,表示最大的能不能被选。
每次合并时,把属性小的合并到属性大的。如果属性小的已经和属性大的一个集合了,就把整个并查集的vis置为1。
否则,如果属性小所在集合的vis已经为1了,那么就把属性大所在集合的vis置为1,否则属性小所在集合的vis置为1。
最后从小到大扫到vis为0时输出就好了

Notice

没什么需要注意的。

Code

用二分图最大匹配解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define sqz main
#define ll long long
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define Rep(i, a, b) for (int i = (a); i < (b); i++)
#define travel(i, u) for (int i = head[u]; ~i; i = edge[i].next)

const ll INF = 1e9, Mo = 998244353;
const int N = 1000000;
const double eps = 1e-6;
namespace slow_IO
{
ll read()
{
ll x = 0; int zf = 1; char ch = getchar();
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x * zf;
}
void write(ll y)
{
if (y < 0) putchar('-'), y = -y;
if (y > 9) write(y / 10);
putchar(y % 10 + '0');
}
}
using namespace slow_IO;

int head[N + 5], flag[N + 5], match[N + 5];
int edgenum = 0, ans, T = 0;
struct node
{
int vet, next;
}edge[2 * N + 5];
void addedge(int u, int v)
{
edge[edgenum].vet = v;
edge[edgenum].next = head[u];
head[u] = edgenum++;
}

int Hungarian(int u)
{
travel(i, u)
{
int v = edge[i].vet;
if (flag[v] == ans) continue;
flag[v] = ans;
if (!match[v] || Hungarian(match[v]))
{
match[v] = u;
return 1;
}
}
return 0;
}

int sqz()
{
int n = read();
rep(i, 1, 10000) head[i] = -1;
rep(i, 1, n)
{
int a = read(), b = read();
addedge(a, i), addedge(b, i);
T = max(T, max(a, b));
}
for (ans = 1; ; ans++)
if (ans > T || !Hungarian(ans))
{
printf("%d\n", --ans);
return 0;
}
return 0;
}

用并查集来解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define sqz main
#define ll long long
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define Rep(i, a, b) for (int i = (a); i < (b); i++)
#define travel(i, u) for (int i = head[u]; ~i; i = edge[i].next)

const ll INF = 1e9, Mo = 998244353;
const int N = 1000000;
const double eps = 1e-6;
namespace slow_IO
{
ll read()
{
ll x = 0; int zf = 1; char ch = getchar();
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
if (ch == '-') zf = -1, ch = getchar();
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return x * zf;
}
void write(ll y)
{
if (y < 0) putchar('-'), y = -y;
if (y > 9) write(y / 10);
putchar(y % 10 + '0');
}
}
using namespace slow_IO;

int vis[N + 5], Fa[N + 5];
int Find(int x)
{
return Fa[x] == x ? x : Fa[x] = Find(Fa[x]);
}

int sqz()
{
int n = read();
rep(i, 1, n + 1) Fa[i] = i;
rep(i, 1, n)
{
int fx = Find(read()), fy = Find(read());
if (fx == fy) vis[fx] = 1;
else
{
if (fx > fy) swap(fx, fy);
if (!vis[fx]) vis[fx] = 1;
else vis[fy] = 1;
Fa[fx] = fy;
}
}
rep(i, 1, n + 1)
if (!vis[i])
{
printf("%d\n", i - 1);
return 0;
}
return 0;
}

文章目录
  1. 1. Description
  2. 2. Solution
  3. 3. Notice
  4. 4. Code