并查集可以动态维护若干个不重叠的集合,支持合并与查询操作,是一种树形的数据结构
村村通
#include
#include
using namespace std;
const int N = 1010;
int p[N], st[N];
int find(int x)
{
if(x == p[x]) return x;
return p[x] = find(p[x]);
}
int main()
{
// freopen("1.in", "r", stdin);
while(1)
{
memset(st, 0, sizeof st);
int n, ans = 0;
scanf("%d", &n);
if(n == 0) return 0;
else
{
int m; scanf("%d", &m);
for(int i = 1; i <= n; ++ i) p[i] = i;
for(int i = 0; i < m; ++ i)
{
int x, y; scanf("%d%d", &x, &y);
int a = find(x);
int b = find(y);
if(a != b) p[a] = b;
}
for(int i = 1; i <= n; ++ i)
{
int x = find(i);
if(!st[x]) ans ++, st[x] = 1;;
}
cout << ans - 1 << endl;
}
}
}
推导部分和
#include
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
//p[N]数组用来做并查集
int p[N], n, m, q;
//s[N]数组是当前点到根结点的权值和,也是前缀和
LL s[N];
//并查集的查询操作(带路径压缩)
int find(int x)
{
if(x == p[x]) return x;
else
{
int t = find(p[x]);
s[x] += s[p[x]];
p[x] = t;
return t;
}
}
int main()
{
// freopen("1.in", "r", stdin);
cin >> n >> m >> q;
for(int i = 1; i <= n; ++ i) p[i] = i;
for(int i = 0; i < m; ++ i)
{
int l ,r;
LL k;
cin >> l >> r >> k;
int t1 = find(l - 1), t2 = find(r);
if(t1 != t2)
{
p[t2] = t1;
s[t2] = s[l - 1] - s[r] + k;
}
}
while(q --)
{
int l, r;cin >> l >> r;
int t1 = find(l - 1), t2 = find(r);
if(t1 != t2) puts("UNKNOWN");
else printf("%lld\n", s[r] - s[l - 1]);
}
return 0;
}
#include
using namespace std;
const int N = 2010;
int p[N];
bool st[N];
int find(int x)
{
if(p[x] == x) return p[x];
return p[x] = find(p[x]);
}
void merge(int x, int y)
{
int px = find(x), py = find(y);
if(px != py) p[px] = py;
}
int main()
{
// freopen("1.in", "r", stdin);
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= 2 * n; ++ i) p[i] = i;
for(int i = 0; i < m; ++ i)
{
char op; int x, y;
cin >> op >> x >> y;
if(op == 'F')
{
merge(x, y);
// merge(x + n, y + n);
}
else
{
merge(x + n, y);
merge(x, y + n);
}
}
// for(int i = 1; i <= n; ++ i) cout << i << ' ' << find(i) << endl;
int cnt = 0;
for(int i = 1; i <= n; ++ i)
if(!st[find(i)])
{
cnt ++;
st[find(i)] = 1;
}
cout << cnt << endl;
return 0;
}
#include
using namespace std;
const int N = 5e4 + 10;
int p[N * 3], ans, k, n;
//1--n代表x的同类,n + 1 -- 2n代表x的食物, 2n + 1 -- 3n代表x的天敌
int find(int x)
{
if(x == p[x]) return x;
return p[x] = find(p[x]);
}
void merge(int x, int y)
{
int px = find(x), py = find(y);
p[py] = px;
}
int main()
{
cin >> n >> k;
for(int i = 1; i <= 3 * n; ++ i) p[i] = i;
for(int i = 0; i < k; ++ i)
{
int d, x, y;scanf("%d%d%d", &d, &x, &y);
if(x > n || y > n) ans ++;
//x、y是同类
else if(d == 1)
{
//如果根据前面的信息,我们可以知道y在x的食物域,
//或者y在x的天敌域中,说明这句话是假话
if(find(x + n) == find(y) || find(x + n + n) == find(y)) ans ++;
//如果根据前面的信息,不能判断这句话是错误的,那么就讲这句话
//当成对的并且更新x的三个域
else
{
//y在x的同类域中
merge(x, y);
//y的食物和x的食物是同类
merge(x + n, y + n);
//y的天敌和x的天敌是同类
merge(x + n + n, y + n + n);
}
}
//如果x吃y
else
{
//若果y在x的同类域或者,y在x的天敌域说明这句话是假话
if(find(x) == find(y) || find(x + n + n) == find(y)) ans ++;
//这句话是真话就更新x的三个域
else
{
//y在x的食物域中
merge(x + n, y);
//y的食物是x的天敌
merge(x + n + n, y + n);
//y的天敌是x的同类
merge(x, y + n + n);
}
}
}
cout << ans << endl;
return 0;
}
牛客修棋盘
#include
#include
#define x first
#define y second
using namespace std;
typedef pairPII;
const int N = 1010;
int res, n, m;
char g[N][N];
bool st[N][N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= m; ++ j)
{
char ch; cin >> ch;
int x = ch -'0';
if((i + j) % 2 == 0 && x == 1) res ++;
if((i + j) % 2 && x == 0) res ++;
}
if(res == m * n) puts("0");
else cout << res << endl;
return 0;
}
P5937 [CEOI1999] Parity Game
题目的具体思路如下:考虑一段连续区间的1个数的奇偶,可以转化为考虑考虑两个端点前缀和的奇偶性上,分为两种情况,如果[l, r]区间内1的个数为偶数,说明sum[r]和sum[l - 1]包含1的个数的奇偶性相同,反之若为奇数则两个包含1的个数的奇偶性相反,我们知道奇偶性具有传递性,这样我们就可以种类并查集来维护,注意n的范围比较大,但是实际的需要用到的点的个数是比较小的,这里我们需要离散化一下。
#include
#define LL long long
using namespace std;
const int N = 1e4 + 10;
int p[N * 2 + 10], n, m, k;
mapmp;
int b[N * 2];
struct wy
{
int l, r, op;
}q[N];
int find(int x)
{
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
void merge(int x, int y)
{
int px = find(x), py = find(y);
p[py] = px;
}
int query(int x)
{
return lower_bound(b + 1, b + 1 + k, x) - b;
}
void solve()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++ i)
{
int l, r, op;
char s[5];
scanf("%d%d%s", &l, &r, s);
if(s[0] == 'e') op = 1;
else op = 2;
q[i] = {--l, r, op};
b[++ k] = l;
b[++ k] = r;
}
sort(b + 1, b + 1 + k);
k = unique(b + 1, b + 1 + k) - (b + 1);
for(int i = 1; i <= 2 * k; ++ i) p[i] = i;
for(int i = 1; i <= m; ++ i)
{
int l = query(q[i].l), r = query(q[i].r), op = q[i].op;
if(op == 1)
{
if(find(l) == find(r + k) || find(r) == find(l + k))
{
printf("%d", i - 1);
return;
}
else
{
merge(l, r);
merge(l + k, r + k);
}
}
else
{
if(find(l) == find(r) || find(r + k) == find(l + k))
{
printf("%d", i - 1);
return;
}
else
{
merge(l, r + k);
merge(r, l + k);
}
}
}
printf("%d", m);
}
int main()
{
// freopen("1.in", "r", stdin);
solve();
return 0;
}
P1955 [NOI2015] 程序自动分析
这道题目是相等关系,相等关系也具有传递性,明显我们可以用并查集来维护。
我们可以先对处理相等,然后去查询不相等的是否在一个集合里面如果在一个集合里面则说明这样的点是不存在的。这道题目的数据的范围很大,但实际用到的很少,我们需要对数据进行离散化。
#include
#define LL long long
using namespace std;
const int N = 1e6 + 10;
int n, m, p[N], a[N], k, tot;
struct wy{
int x, y, e;
}q[N];
int find(int x)
{
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
void merge(int x, int y)
{
int px = find(x), py = find(y);
p[py] = px;
}
void solve()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++ i){
int x, y, e;
scanf("%d%d%d", &x, &y, &e);
a[++ tot] = q[i].x = x;
a[++ tot] = q[i].y = y;
q[i].e = e;
}
sort(a + 1, a + 1 + tot);
tot = unique(a + 1, a + 1 + tot) - a - 1;
for(int i = 1; i <= tot; ++ i) p[i] = i;
for(int i = 1; i <= n; ++ i)
{
q[i].x = lower_bound(a + 1, a + tot + 1, q[i].x) - a - 1;
q[i].y = lower_bound(a + 1, a + tot + 1, q[i].y) - a - 1;
if(q[i].e == 1){
merge(q[i].x, q[i].y);
}
}
for(int i = 1; i <= n; ++ i){
int x = q[i].x;
int y = q[i].y;
if(q[i].e == 0 && find(x) == find(y)){
puts("NO");
return;
}
}
puts("YES");
}
int main()
{
// freopen("1.in", "r", stdin);
scanf("%d", &k);
while(k--) solve();
return 0;
}