并查集无疑是一个很基础的算法, 它的主要意义是快速地插入或询问两个数之间的关系。
poj1112 小胖的奇偶
刚接触的话这道题的确是相当的厉害啊!
维护一段区间的奇偶性, 也就是从Y 到 X 这段是0 还是1 。
设有关系 f(x, y), f(y, z), 那么 f(x, z) == (f(x, y) + f(y, z))%2;
这里的办法是每个点都开两个点f(x), f(x + n)
然后因为n很大所以要再写一个hash就好了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define mod 6000
using namespace std;
int n, m, map[mod + 100], fat[mod * 5];
int hash(int x){
int t = x % mod;
while(map[t] != -1 && map[t] != x)t = (t + 1) % mod;
map[t] = x; return t;
}
int find(int x){
if(!fat[x]) return x;
return fat[x] = find(fat[x]);
}
void uu(int a, int b){
int x = find(a), y = find(b);
if(x != y)fat[x] = y;
}
int main()
{
scanf("%d%d", &n, &m); int i;
memset(map, -1, sizeof(map));
int a, b; char s[10];
for(i = 1; i <= m; i ++){
scanf("%d%d%s", &a, &b, s);
int flag = 0;if(s[0] == 'o')flag = 1;
a = hash(a - 1); b = hash(b);
if(flag){
if(find(a) == find(b)) break;
uu(a, b + mod + 1); uu(a + mod + 1, b);
}
else{
if(find(a) == find(b + mod + 1))break;
uu(a, b); uu(a + mod + 1, b + mod + 1);
}
}printf("%d\n", i - 1);
// system("pause");
return 0;
}
vj 1531 食物链
最经典的并查集了吧, 和刚才那道题类似! 就是开三个点就好了
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define MAXN 50005
using namespace std;
int n, k, fat[MAXN * 3], ans;
int find(int x){
if(!fat[x])return x;
return (fat[x] = find(fat[x]));
}
void uu(int x, int y){
x = find(x), y = find (y);
if(x != y)fat[y] = x;
}
int main()
{
scanf("%d%d", &n, &k);
int d, x, y;
for(int i = 1; i <= k; i ++){
scanf("%d%d%d", &d, &x, &y);
if(x > n || y > n || (d == 2 && x == y)){ans ++; continue;}
if(d == 1){
if(find(x) == find(y + n) || find(x) == find(y + n + n)){ans ++; continue;}
uu(x, y); uu(x + n, y + n); uu(x + n + n, y + n + n);
}
else{
if(find(x) == find(y) || find(x) == find(y + n + n)){ans ++; continue;}
uu(x, y + n); uu(x + n, y + n + n); uu(x + n + n, y);
}
}cout<<ans<<endl;
// system("pause");
return 0;
}
所以又写了个偏移向量的
我们用一个dir[]数组表示每一个物种与他上级的关系,我们很容易能发现
利用a,b b,c的关系,可以推导出a,c的关系!
我定义如果i和上级是同物种,dir[i] = 0
i吃上级,dir[i] = 1;
i被上级吃,dir[i] = 2;
为什么这么定义呢?(其实你也可以把值改变,但关系传递时你就慢慢写if来判断吧)
我的合并操作还是把a所在树当做b的子树,那么a的上级与b的关系也可以根据a,b的关系&&a和上级的关系推出
代码:
- #include <iostream>
- #include <cstdio>
- #include <cstdlib>
- using namespace std;
- #define MAX (50000+1)
- int n,m,mot[MAX],dir[MAX],ans;
- int find(int x)
- {
- if ( x != mot[x] )
- {
- int fat = mot[x];
- mot[x] = find(mot[x]);
- dir[x] += dir[fat];
- dir[x] %= 3;
- }
- return mot[x];
- }
- void merge(int a,int b,int c)
- {
- if ( a == mot[a] )
- {
- mot[a] = b;
- dir[a] = c;
- }
- else
- {
- mot[ mot[a] ] = b;
- dir[ mot[a] ] = ((c + 9) - dir[a]) % 3;
- }
- }
- int main()
- {
- scanf("%d %d",&n,&m);
- for (int i = 1; i <= n; i++)
- {
- mot[i] = i;
- }
- int swi,a,b;
- for (int i = 1; i <= m; i++)
- {
- scanf("%d %d %d",&swi,&a,&b);
- if ( a > n || b > n )
- {
- ans++;
- }
- else if ( swi == 1 )
- {
- if ( find(a) == find(b) )
- {
- if ( dir[a] != dir[b] )
- {
- ans++;
- }
- }
- else
- {
- merge(a,b,0);
- }
- }
- else
- {
- if ( find(a) == find(b) )
- {
- if ( ( 1 + dir[b] ) % 3 != dir[a] )
- {
- ans++;
- }
- }
- else
- {
- merge(a,b,1);
- }
- }
- }
- cout<<ans;
- return 0;
- }
bzoj 1202
其实做过了前两题之后再看这道题就很显然了! 但我最开始还是错了。。说明要加强练习。
开两个数组一个记录一个点的根节点一个记录该点与根节点之间的关系。
进一步地了解了并查集: 在多个连通块中, l常数 的时间查询两个点是否在一个连通块内以及一个连通块中两个点的关系。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define MAXN 105
using namespace std;
int t, n, m, fat[MAXN], ff[MAXN];
int find(int x){
if(fat[x] != x){
int tmp = fat[x];
fat[x] = find(fat[x]);
ff[x] += ff[tmp];
} return fat[x];
}
int main()
{
scanf("%d", &t); while(t --){
scanf("%d%d", &n, &m);
for(int i = 0; i <= n; i ++)fat[i] = i;
memset(ff, 0, sizeof(ff));
bool flag = 1;
for(int i = 1; i <= m; i ++){
if(!flag)break;
int a, b, x; scanf("%d%d%d", &a, &b, &x); a --;
int fa = find(a), fb = find(b);
if(fa != fb){
fat[fb] = fa; ff[fb] = ff[a] - ff[b] + x;
}
else if(ff[b] - ff[a] != x)flag = 0;
}if(flag)puts("true");else puts("false");
}
return 0;
}
hcs的题。。基本上和上一道一样!机智的hcs !!
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define MAXN 100005
using namespace std;
int n, m, k, ans, fat[MAXN], ff[MAXN];
int find(int x){
if(fat[x] != x){
int tmp = fat[x];
fat[x] = find(fat[x]);
ff[x] = (ff[x] + ff[tmp]) % k;
} return fat[x];
}
int main()
{
scanf("%d%d%d", &n, &m, &k); k ++;
for(int i = 0; i <= n; i ++)fat[i] = i;
int c, a, b;
for(int i = 1; i <= m; i ++){
scanf("%d%d%d", &c, &a, &b);a --;
int fa = find(a), fb = find(b);
if(fa != fb){
fat[fb] = fa; ff[fb] = (ff[a] - ff[b] + k + c) % k;
}
else if(((ff[b] - ff[a] + k) % k) != c)ans ++;
}double aa = (double)(m - ans) / (double)m;
printf("%.5lf\n", aa);
return 0;
}