kuangbin带你飞 并查集 题解
做这套题之前一直以为并查集是很简单的数据结构。
做了才发现自己理解太不深刻。只看重片面的合并集合。。
重要的时发现每个集合的点与这个根的关系,这个关系可以做太多事情了。
题解:
POJ 2236 Wireless Network
10S时限,尽量做到最优吧。一开始还怕会T
预处理出能到达的点。简单题看代码就行了
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
struct point
{
double x,y;
}src[MAXN];
int fa[MAXN];
int Find(int x) {return x == fa[x] ? x : fa[x] = Find(fa[x]);}
bool repair[MAXN];
double dis[MAXN][MAXN];
vector<int>go[MAXN]; int main()
{
int N;
double D;
scanf("%d%lf",&N,&D);
for (int i = ; i <= N ; i++) scanf("%lf%lf",&src[i].x,&src[i].y);
for (int i = ; i <= N ; i++) go[i].clear();
for (int i = ; i <= N ; i++) fa[i] = i;
for (int i = ; i <= N ; i++)
{
for (int j = i + ; j <= N ; j++)
{
dis[i][j] = sqrt((src[i].x - src[j].x) * (src[i].x - src[j].x) + (src[i].y - src[j].y) * (src[i].y - src[j].y));
dis[j][i] = dis[i][j];
if (dis[i][j] <= D) go[i].push_back(j);
if (dis[i][j] <= D) go[j].push_back(i);
}
} memset(repair,false,sizeof(repair));
char op[];
while (scanf("%s",op) != EOF)
{
if(op[] == 'O')
{
int x;
scanf("%d",&x);
repair[x] = true;
for (int j = ; j < (int)go[x].size() ; j++)
{
if (repair[go[x][j]])
{
int fx = Find(x);
int fy = Find(go[x][j]);
if (fx != fy) fa[fx] = fy;
}
}
}
else
{
int x,y;
scanf("%d%d",&x,&y);
int fx = Find(x);
int fy = Find(y);
if (fx == fy) puts("SUCCESS");
else puts("FAIL");
}
}
return ;
}
POJ 1611 The Suspects
有一个学校,有N个学生,编号为0-N-1,现在0号学生感染了非典,凡是和0在一个社团的人就会感染,并且这些人如果还参加了别的社团,他所在的社团照样全部感染,求感染的人数。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
int N,M;
int main()
{
while (scanf("%d%d",&N,&M) != EOF)
{
if (N == && M == ) break;
for (int i = ; i <= N ; i++) fa[i] = i;
for (int i = ; i < M ; i++)
{
int cnt;
scanf("%d",&cnt);
int x;
scanf("%d",&x);
int fx = Find(x);
for (int i = ; i < cnt ; i++)
{
int u;
scanf("%d",&u);
int fu = Find(u);
if (fx != fu) fa[fu] = fx;
}
}
int ret = ;
int need = Find();
for (int i = ; i < N ; i++)
{
int fi = Find(i);
if (fi == need) ret++;
}
printf("%d\n",ret);
}
return ;
}
HDU 1213 How Many Tables
有几个集合
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
int N,M;
bool vis[MAXN]; int main()
{
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&N,&M);
memset(vis,false,sizeof(vis));
for (int i = ; i <= N ; i++) fa[i] = i;
while (M--)
{
int x,y;
scanf("%d%d",&x,&y);
int fx = Find(x);
int fy = Find(y);
if (fx != fy) fa[fx] = fy;
}
int ret = ;
for (int i = ; i <= N ; i++)
if (i == Find(i)) ret++;
printf("%d\n",ret);
}
return ;
}
HDU 3038 How Many Answers Are Wrong
第一次接触这类题目,一开始完全没想到要用并查集该怎么做,看了别人的代码才慢慢算是理解?
重要的是维护当前点,与当前点和他的跟之间的关系。这道题就变成了到他根的和为多少
我做这道题始终保持跟的下标是其集合内最小的
这道题的路径压缩是很好理解的。
重要的是怎么合并的两颗树。也就是unionset代码。看别人的这个代码困扰了我很久。最后自己手动代入才明白了一些
首先第一保证跟的下标是其集合内最小的 就是代码里要分论讨论的合并fa[x] = y的x和y 的关系
bool unionset(int x,int y,int val)
{
int a = Find(x);
int b = Find(y);
if (a == b)
{
return sum[y] - sum[x] == val;
}
if (a < b)
{
fa[b] = a;
sum[b] = sum[x] + val - sum[y];
}
else
{
fa[a] = b;
sum[a] = sum[y] - val - sum[x];
}
return true;
}
首先sum[x]表示x到他的根的距离
代码中,对于a > b,在数轴上是这情况。1:b--a--x--y 那么fa[a] = b,就要求最左边那段的长度 就是by - xy - ax = ab
对于 a < b 在数轴上是这种情况 2:a--b--x--y 或者3: a--x--b--y 对于第一个情况使得fa[b] = a表示最左边的长度就是ax + xy - by = ab及代码中的sum[b] = sum[x] + val - sum[y];
对于另一种情况 求ab 等于 ax + xy - xy = ab 同样也是那个式子所以可以合并。那么合并集合就可以了
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int sum[MAXN];
int Find(int x)
{
if (x == fa[x]) return x;
else
{
int tmp = fa[x];
fa[x] = Find(fa[x]);
sum[x] += sum[tmp];
}
return fa[x];
} bool unionset(int x,int y,int val)
{
int a = Find(x);
int b = Find(y);
if (a == b)
{
return sum[y] - sum[x] == val;
}
if (a < b)
{
fa[b] = a;
sum[b] = sum[x] + val - sum[y];
}
else
{
fa[a] = b;
sum[a] = sum[y] - val - sum[x];
}
return true;
} int main()
{
int N,M;
while (scanf("%d%d",&N,&M) != EOF)
{
int ret = ;
for (int i = ; i <= N ; i++) fa[i] = i;
memset(sum,,sizeof(sum));
while(M--)
{
int x,y,val;
scanf("%d%d%d",&x,&y,&val);
if (!unionset(x - ,y,val)) ret++;
}
printf("%d\n",ret);
}
return ;
}
POJ 1182 食物链
这题目是并查集的超经典应用。
说难起始也难,说不难页不难。。!!
首先定义结构体。分别是他的父亲。和他与他这题目是并查集的超经典应用。父亲的关系
relat = 0 表示 其和其父亲是同类
relat = 1 表示 他的父亲吃他
relat = 2 表示 他吃他的父亲
那么初始化就所有值得父亲为他本身,显然他们之间的关系relat = 0本身和本身必然是同类
对于询问假话,如果2个在一个集合里。通过他们和父亲的关系就可以判断他们之间的relat关系来判断假话
第一步 : 路径压缩如何更新
这个你枚举一下就可以发现儿子和爷爷的relat值=(儿子与父亲的relat值+父亲与爷爷的relat值)%3 举个例子。儿子吃父亲 当前relat = 2,爷爷吃父亲,这个的relat = 1,这种情况
表示儿子和爷爷是同类。所以儿子和爷爷的关系是relat为何只考虑3层,考虑路径压缩压成了几层
第二步 : 也是我觉得最困扰的如何合并集合。将2个树合并到一起
先给出代码
if (roota != rootb)
{
p[rootb].fa = roota;
p[rootb].relat = (op - 1 + 6 + p[a].relat - p[b].relat) % 3;
}
当前有a->roota ,b -> rootb a和b之间的op值。根据题意有op = 0,表示2者同类,op - 1是我们预先设置的状态值0,op = 2表示a吃b 。根据我们的设置op - 1 = 1表示父亲吃儿子
使得rootb的父亲是roota 那么相关的更新你可以这么看 把这4个点拉成一条链(首先我不知道这么做到底对不对。虽然AC了)
接下来由这张图来看。我们先求rootb和a之间的关系 由前面儿子和爷爷的结论 。我们快速得到这个值等于 (3 - relat[b]) % 3 + (3 - (OP - 1)) % 3
为什么这样注意:relat[b] 表示b和rootb的关系。不是rootb和b的关系。偏移量值需要改变。同理ab也一样。得到这个值后。这里就可以看做b不存在。三层中有roota--a--rootb
再利用这个公式就得到 ((3 - relat[b]) % 3 + (3 - (OP - 1)) % 3 + relat[a]) % 3 合并一下同余一下就得到了上面的合并的式子
最后判断假话
首先其一定在一个集合内。否则其必然是真话,因为遇到不在一个集合里就可以合并集合
第一种判断他们是不是同一类生物。这个直接判断和跟relat即可
另一种判断吃的关系同样也是你手动枚举一下就知道咋弄了。
代码:
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
struct node
{
int fa;
int relat;
}p[MAXN]; int Find(int x)
{
int tmp;
if (x == p[x].fa) return x;
tmp = p[x].fa;
p[x].fa = Find(p[x].fa);
p[x].relat = (p[x].relat + p[tmp].relat) % ;
return p[x].fa;
} int main()
{
int N,K;
int op;
int ret = ;
scanf("%d%d",&N,&K);
for (int i = ; i <= N ; i++)
{
p[i].fa = i;
p[i].relat = ;
}
for (int i = ; i <= K ; i++)
{
int a,b;
scanf("%d%d%d",&op,&a,&b);
if(a > N || b > N)
{
ret++;
continue;
}
if (op == && a == b)
{
ret++;
continue;
}
int roota = Find(a);
int rootb = Find(b);
if (roota != rootb)
{
p[rootb].fa = roota;
p[rootb].relat = (op - + + p[a].relat - p[b].relat) % ;
}
else
{
if(op == && p[a].relat != p[b].relat)
{
ret++;
continue;
}
if (op == && ( - p[a].relat + p[b].relat) % != )
{
ret++;
}
}
}
printf("%d\n",ret);
return ;
}
POJ 1417 True Liars
并查集+背包dp
神只说真话,魔鬼直说假话。那就就发现一个很重要的事情!
只要回答yes那么2者同类,否则2者异类。这个不解释了
所以可以用并查集来维护点和根的关系。最后问是否方案数目唯一。于是这个就背包来维护
这个详情看代码吧
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int val[MAXN];
bool vis[MAXN];
int Find(int x)
{
if (x == fa[x]) return x;
int tmp = fa[x];
fa[x] = Find(fa[x]);
val[x] = (val[x] + val[tmp]) % ;
return fa[x];
}
int N,P1,P2;
int cnt[MAXN][];
int dp[MAXN][MAXN];
int pre[MAXN][MAXN];
vector<int>ans;
vector<int>res[MAXN][]; int main()
{
while(scanf("%d%d%d",&N,&P1,&P2) != EOF)
{
if (N == && P1 == && P2 == ) break;
for (int i = ; i <= P1 + P2 ; i++)
{
fa[i] = i;
val[i] = ;
}
for (int i = ; i < N ; i++)
{
int x,y;
char op[];
scanf("%d%d%s",&x,&y,op);
int fx = Find(x);
int fy = Find(y);
if (fx != fy)
{
fa[fy] = fx;
int tmp;
if (op[] == 'y') tmp = ;
else tmp = ;
val[fy] = val[x] + val[y] + tmp;
}
}
memset(vis,false,sizeof(vis));
memset(cnt,,sizeof(cnt));
for(int i = ; i <= P1 + P2 ; i++)
{
res[i][].clear();
res[i][].clear();
}
int cas = ;
for (int i = ; i <= P1 + P2 ; i++)
{
if (vis[i]) continue;
int fi = Find(i);//之前写错是因为i这个点不一定是根。我默认这点为跟了。下面就是错误的写法
/*
if (vis[i]) continue;
vis[i] = true;
int fi = Find(i);
res[cas][0].push_back(i);
cnt[cas][0]++;
for (int j = i + 1 ; j <= P1 + P2 ; j++)
*/
for (int j = i ; j <= P1 + P2 ; j++)
{
if (vis[j]) continue;
int fj = Find(j);
if (fj != fi) continue;
vis[j] = true;
res[cas][val[j]].push_back(j);
cnt[cas][val[j]]++;
}
cas++;
}
memset(dp,,sizeof(dp));
cas--;
/* for (int i = 1 ; i <= cas ; i++)
{
for (int j = 0 ; j < (int)res[i][0].size(); j++)
printf("%d ",res[i][0][j]);
puts("");
for (int j = 0 ; j < (int)res[i][1].size(); j++)
printf("%d ",res[i][1][j]);
puts("");
printf("%d %d\n",cnt[i][0],cnt[i][1]);
}
*/
dp[][] = ;
for (int i = ; i <= cas ; i++)
{
for (int j = P1 ; j >= ; j--)
{
if (j >= cnt[i][] && dp[i - ][j - cnt[i][]] > )
{
dp[i][j] += dp[i - ][j - cnt[i][]];
if (dp[i][j] > ) dp[i][j] = ;
pre[i][j] = j - cnt[i][];
}
if (j >= cnt[i][] && dp[i - ][j - cnt[i][]] > )
{
dp[i][j] += dp[i - ][j - cnt[i][]];
if (dp[i][j] > ) dp[i][j] = ;
pre[i][j] = j - cnt[i][];
}
}
}
if (dp[cas][P1] != )
{
puts("no");
continue;
}
ans.clear();
int cur = P1;
for (int i = cas ; i >= ;i--)
{
int pref = pre[i][cur];
int sub = cur - pref;
if (cnt[i][] == sub)
{
for (int j = ; j < (int)res[i][].size(); j++)
ans.push_back(res[i][][j]);
}
else
{
for (int j = ; j < (int)res[i][].size() ; j++)
ans.push_back(res[i][][j]);
}
cur = pref;
}
sort(ans.begin(),ans.end());
for (int i = ; i < (int)ans.size() ; i++)
printf("%d\n",ans[i]);
puts("end");
}
return ;
}
POJ 1456 Supermarket
这个题数据这么小。怎么做都行。并查集就帮助你理解并查集
首先这是一个贪心
肯定优先选择大的完成。。贪心很明显,因为每个物品只需要执行一天,且全部都是一天
并查集维护点到根之间最近的可用的时间点是什么 ,根下标小与等于树上的点
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
struct node
{
int d,p;
friend bool operator < (const node &a,const node &b)
{
return a.p > b.p;
}
}src[MAXN];
int N; int main()
{
while (scanf("%d",&N) != EOF)
{
for (int i = ; i < N ; i++) scanf("%d%d",&src[i].p,&src[i].d);
sort(src,src + N);
for (int i = ; i < MAXN ; i++) fa[i] = i;
int ret = ;
for (int i = ; i < N ; i++)
{
int ti = Find(src[i].d);
if (ti > )
{
ret += src[i].p;
fa[ti] = ti - ;
}
}
printf("%d\n",ret);
}
return ;
}
POJ 1733 Parity game
同样询问假话在哪
并查集维护点到根之间1的个数是奇数还是偶数。有了前面的铺垫这里就很简单了。
val = 0 儿子到父亲之间为偶数个1,val = 1 儿子到父亲之间为奇数个1
第一:路径压缩 : 直接儿子val异或父亲val值。
第二:合并集合 : 直接全异或。
首先我保证 父亲的下标小于儿子。对于a,b之间的一个操作(奇数或者偶数)那么可能的情况是 fb--b--a--fa直接求fa到fb的奇偶性,无论什么情况直接val[b] ^ val[a] ^ ab即可对于其他数轴上的情况
一样的式子
代码:
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN],val[MAXN];
map<int,int>mp;
int L;
int u[MAXN],v[MAXN],ask[MAXN];
// val = 0 even val = 1 odd int Find(int x)
{
if (x == fa[x]) return x;
int tmp = fa[x];
fa[x] = Find(fa[x]);
val[x] = val[x] ^ val[tmp];
return fa[x];
} int main()
{
int N;
int cas = ;
while (scanf("%d",&L) != EOF)
{
mp.clear();
scanf("%d",&N);
int ans = N;
for (int i = ; i < N ; i++)
{
scanf("%d%d",&u[i],&v[i]);
if (u[i] > v[i]) swap(u[i],v[i]);
u[i]--;
if (!mp[u[i]]) mp[u[i]] = cas++;
if (!mp[v[i]]) mp[v[i]] = cas++;
char op[];
scanf("%s",op);
if (op[] == 'e') ask[i] = ;
else ask[i] = ;
}
for (int i = ;i < MAXN ; i++)
{
fa[i] = i;
val[i] = ;
}
for (int i = ; i < N ; i++)
{
int l = mp[u[i]];
int r = mp[v[i]];
int fl = Find(l);
int fr = Find(r);
if (fl == fr)
{
if ((val[l] ^ val[r]) != ask[i])
{
ans = i;
break;
}
}
else
{
fa[fr] = fl;
val[fr] = val[l] ^ val[r] ^ ask[i];
}
}
printf("%d\n",ans);
}
return ;
}
POJ 1984 Navigation Nightmare
题很难读懂。这个题卡了我好久。我一直看别人代码十分费解。这个向量偏移,相对值到底该怎么理解
真是日了X了
这里都是相对长度,首先只分析南北方向。东西方向一样的分开分析
第一步:路径压缩:儿子相应长度值+=父亲相应长度值
第二部: 合并集合: 就这里我百思不得其解。
下面坐标全部指的是y坐标
首先保证根的坐标在节点坐标的下方。对于向北(上)方向的直线,认为他是正长度,否则为副长度
4个点 : r1,r2,f1,f2 r1的根为f1,r2的根为f2
则有 f2 到 f1 的长度 + r2 到 f2 的长度 =
r1 到 r2 的长度 + r1 到 f1 的长度 = 向量 r2-f1的长度
东西方向同理
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int dx[MAXN],dy[MAXN];
int from[MAXN],to[MAXN],L[MAXN];
char dir[MAXN][];
struct node
{
int u,v;
int index;
int ti;
friend bool operator < (const node &a,const node &b)
{
return a.ti < b.ti;
}
}src[MAXN];
int ans[MAXN];
int Find(int x)
{
if (x == fa[x]) return x;
int tmp = fa[x];
fa[x] = Find(fa[x]);
dy[x] += dy[tmp];
dx[x] += dx[tmp];
return fa[x];
} int main()
{
int N,M;
int Q;
while (scanf("%d%d",&N,&M) != EOF)
{
for(int i = ; i <= N ; i++) fa[i] = i;
memset(dx,,sizeof(dx));
memset(dy,,sizeof(dy));
for (int i = ; i <= M ; i++)
{
scanf("%d%d%d",&from[i],&to[i],&L[i]);
scanf("%s",dir[i]);
}
scanf("%d",&Q);
for (int i = ; i < Q ; i++)
{
scanf("%d%d%d",&src[i].u,&src[i].v,&src[i].ti);
src[i].index = i;
}
sort(src,src + Q);
int t = ;
for (int i = ; i < Q ; i++)
{
while (t <= M && src[i].ti >= t)
{
int f1 = Find(from[t]);
int f2 = Find(to[t]);
if (f1 != f2)
{
fa[f2] = f1;
if (dir[t][] == 'N')
{
dy[f2] = L[t] + dy[from[t]] - dy[to[t]];
dx[f2] = dx[from[t]] - dx[to[t]];
}
else if (dir[t][] == 'S')
{
dy[f2] = -L[t] + dy[from[t]] - dy[to[t]];
dx[f2] = dx[from[t]] - dx[to[t]];
}
else if (dir[t][] == 'E')
{
dx[f2] = L[t] + dx[from[t]] - dx[to[t]];
dy[f2] = dy[from[t]] - dy[to[t]];
}
else
{
dx[f2] = -L[t] + dx[from[t]] - dx[to[t]];
dy[f2] = dy[from[t]] - dy[to[t]];
}
}
t++;
}
if (Find(src[i].u) != Find(src[i].v)) ans[src[i].index] = -;
else
{
ans[src[i].index] = abs(dx[src[i].u] - dx[src[i].v]) + abs(dy[src[i].u] - dy[src[i].v]);
}
}
for (int i = ; i < Q ; i++) printf("%d\n",ans[i]);
}
return ;
}
POJ 2492 A Bug's Life
给出交配行为,问是否同性可交配
默认每一次交配为异性,如果有冲突则满足有同性交配
同样和父亲的关系为同性 val值为0,否则为1
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXD = ;
int fa[MAXN];
int val[MAXN]; int Find(int x)
{
if (x == fa[x]) return x;
int tmp = fa[x];
fa[x] = Find(fa[x]);
val[x] = val[x] ^ val[tmp];
return fa[x];
} int main()
{
// freopen("sample.txt","r",stdin);
int T,N,M,kase = ;
bool first = true;
scanf("%d",&T);
while (T--)
{
if (first) first = false;
else putchar('\n');
scanf("%d%d",&N,&M);
for (int i = ; i <= N ; i++)
{
fa[i] = i;
val[i] = ;
}
bool flag = false;
for (int i = ; i < M ; i++)
{
int u ,v;
scanf("%d%d",&u,&v);
if (flag) continue;
int fu = Find(u);
int fv = Find(v);
if (fu == fv)
{
int res = val[u] ^ val[v];
if (res == )
{
flag = true;
}
}
else
{
fa[fv] = fu;
val[fv] = val[u] ^ val[v] ^ ;
}
}
printf("Scenario #%d:\n",kase++);
if (flag) puts("Suspicious bugs found!");
else puts("No suspicious bugs found!");
}
return ;
}
POJ 2912 Rochambeau
难点是要注意到N很小只有500,那么可以枚举裁判是谁。然后用类似食物链的方法并查集维护处假话。
分情况输出,如果只有一个裁判的情况下。那么分辨出的步数就是不是确定裁判的所有枚举中的最大矛盾round
怎么维护并查集看前面。
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int val[MAXN]; int Find(int x)
{
if (x == fa[x]) return x;
int tmp = fa[x];
fa[x] = Find(fa[x]);
val[x] = (val[x] + val[tmp]) % ;
return fa[x];
} int N,M;
struct node
{
int u,v;
int op;
}src[];
char str[]; int main()
{
while (scanf("%d%d",&N,&M) != EOF)
{
gets(str);
for (int i = ; i < M ; i++)
{
gets(str);
int pos;
int len = strlen(str);
for (pos = ; pos < len ; pos++) if (str[pos] == '=' || str[pos] == '>' || str[pos] == '<') break;
int u = ;
for (int j = ; j < pos ; j++)
{
u = u * + str[j] - '';
}
int v = ;
for (int j = pos + ; j < len ; j++)
{
v = v * + str[j] - '';
}
src[i].u = u;
src[i].v = v;
if (str[pos] == '=') src[i].op = ;
else if (str[pos] == '<') src[i].op = ;
else src[i].op = ;
}
// for (int i = 0 ; i < M ; i++)
// printf("%d %d %d\n",src[i].u,src[i].v,src[i].op);
int ansi,anst = ;
int cnt = ;
for (int i = ; i < N ; i++)
{
for (int j = ; j < N ; j++) fa[j] = j;
memset(val,,sizeof(val));
int pos = -;
for (int j = ; j < M ; j++)
{
if (src[j].u == i || src[j].v == i) continue;
int u = src[j].u;
int v = src[j].v;
int fu = Find(u);
int fv = Find(v);
if (fu == fv)
{
if (( - src[j].op + val[u]) % != val[v])
{
pos = j + ;
break;
}
}
else
{
fa[fv] = fu;
val[fv] = ( - val[v] - src[j].op + val[u]) % ;
}
}
if (pos == -)
{
cnt++;
ansi = i;
}
else anst = max(anst,pos);
}
if(cnt == )printf("Impossible\n");
else if(cnt >= )printf("Can not determine\n");
else
printf("Player %d can be determined to be the judge after %d lines\n",ansi,anst); }
return ;
}
ZOJ 3261 Connections in Galaxy War
正向在线做的话有删除操作很难处理,就离线变成从后向前处理变为添加操作
就是普通并查集
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
const int MAXD = ;
int fa[MAXN];
int Find(int x) {return x == fa[x] ? x : fa[x] = Find(fa[x]);}
int val[MAXN],pos[MAXN];
void unionset(int u,int v)
{
int fu = Find(u),fv = Find(v);
if (fu != fv)
{
fa[fu] = fv;
if (val[fu] > val[fv])
{
val[fv] = val[fu];
pos[fv] = pos[fu];
}
else if (val[fu] == val[fv] && pos[fu] < pos[fv])
{
pos[fv] = pos[fu];
}
}
}
int from[],to[];
map<int,int>mp[MAXN];
bool vis[];
int p[MAXN];
struct node
{
int op;
int u,v;
}ask[MAXD];
int ret[MAXD]; int main()
{
int N;
bool first = true;
while (scanf("%d",&N) != EOF)
{
if (first) first = false;
else puts("");
for (int i = ; i < N ; i++)
{
scanf("%d",&p[i]);
val[i] = p[i];
pos[i] = i;
mp[i].clear();
}
for (int i = ; i <= N ; i++) fa[i] = i;
int M;
scanf("%d",&M);
for (int i = ; i <= M ; i++)
{
scanf("%d%d",&from[i],&to[i]);
if (from[i] > to[i]) swap(from[i],to[i]);
mp[from[i]][to[i]] = i;
vis[i] = false;
}
int Q;
scanf("%d",&Q);
for (int i = ; i < Q ; i++)
{
char op[];
scanf("%s",op);
if(op[] == 'q')
{
ask[i].op = ;
scanf("%d",&ask[i].u);
}
else
{
ask[i].op = ;
scanf("%d%d",&ask[i].u,&ask[i].v);
if (ask[i].u > ask[i].v) swap(ask[i].u,ask[i].v);
int tmp = mp[ask[i].u][ask[i].v];
vis[tmp] = true;
}
}
for (int i = ; i <= M ; i++)
{
if(!vis[i]) unionset(from[i],to[i]);
}
int tot = ;
for (int i = Q - ; i >= ; i--)
{
if (ask[i].op == )
{
int fu = Find(ask[i].u);
if (val[fu] > p[ask[i].u]) ret[tot++] = pos[fu];
else ret[tot++] = -;
}
else
{
unionset(ask[i].u,ask[i].v);
}
}
tot--;
for (int i = tot ; i >= ; i--) printf("%d\n",ret[i]);
}
return ;
}
HDU 1272 小希的迷宫
一个无向图是不是树。注意坑点 第一:森林,第二:空树
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int res[MAXN * ];
int tot;
int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
int main()
{
// freopen("sample.txt","r",stdin);
int u,v;
while (scanf("%d%d",&u,&v) != EOF)
{
if (u == - && v == -) break;
if (u == && v == )
{
puts("Yes");
continue;
}
for (int i = ; i < MAXN ; i++) fa[i] = i;
bool flag = false;
int fu = Find(u);
int fv = Find(v);
if (fu != fv) fa[fv] = fu;
tot = ;
res[tot++] = u;
res[tot++] = v;
while (scanf("%d%d",&u,&v) != EOF)
{
if (u == && v == ) break;
if (flag) continue;
int fu = Find(u);
int fv = Find(v);
res[tot++] = u;
res[tot++] = v;
if (fu == fv) flag = true;
else
{
fa[fv] = fu;
}
}
sort(res,res + tot);
tot = unique(res,res + tot) - res;
// for (int i = 0 ; i < tot ; i++) printf("%d ",res[i]); puts("");
int cnt = ;
for (int i = ; i < tot ; i++)
{
if (Find(res[i]) == res[i])
cnt++;
if (cnt >= )
{
flag = true;
break;
}
}
if (flag) puts("No");
else puts("Yes");
}
return ;
}
POJ 1308 Is It A Tree?
有向图是不是树
连通后仅有一个点的入度为0,其他所有点的入度都为1 即为树
#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <stack>
#include <queue>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <climits>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define PI 3.1415926535897932626
using namespace std;
int gcd(int a, int b) {return a % b == ? b : gcd(b, a % b);}
const int MAXN = ;
int fa[MAXN];
int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
int deg[MAXN];
bool vis[MAXN]; //这题用并查集判断连通,连通后有且仅有1个入度为0,其余入度为1,就是树了。 //注意树为空的情况。 int main()
{
int u,v,kase = ;
while (scanf("%d%d",&u,&v) != EOF)
{
if (u == - && v == -) break;
if (u == && v == )
{
printf("Case %d is a tree.\n",kase++);
continue;
}
memset(vis,false,sizeof(vis));
vis[u] = vis[v] = true;
memset(deg,,sizeof(deg));
deg[v]++;
for (int i = ; i < MAXN ; i++) fa[i] = i;
fa[v] = u;
while (scanf("%d%d",&u,&v) != EOF)
{
if (u == && v == ) break;
int fu = Find(u);
int fv = Find(v);
if (fu != fv)
{
fa[fv] = fu;
}
vis[u] = vis[v] = true;
deg[v]++;
}
int num = ,cnt0 = ,cnt1 = ;
int pos;
bool flag = false;
for (int i = ; i < MAXN ; i++)
{
if (vis[i])
{
pos = i;
break;
}
}
int need = Find(pos);
for (int i = pos ; i < MAXN ; i++)
{
if (vis[i])
{
num++;
if (deg[i] == ) cnt0++;
else if (deg[i] == ) cnt1++;
if (Find(i) != need)
{
flag = true;
break;
}
}
}
if (cnt0 == && cnt1 + cnt0 == num && !flag) printf("Case %d is a tree.\n",kase++);
else printf("Case %d is not a tree.\n",kase++);
}
return ;
}
kuangbin带你飞 并查集 题解的更多相关文章
- Kuangbin 带你飞-基础计算几何专题 题解
专题基本全都是模版应用.贴一下模版 平面最近点对 const double INF = 1e16; ; struct Point { int x,y; int type; }; double dist ...
- Kuangbin 带你飞专题十一 网络流题解 及模版 及上下界网络流等问题
首先是几份模版 最大流:虽然EK很慢但是优势就是短.求最小割的时候可以根据增广时的a数组来判断哪些边是割边.然而SAP的最大流版我只会套版,并不知道该如何找到这个割边.在尝试的时候发现了一些问题.所以 ...
- Kuangbin 带你飞-线段树专题 题解
HDU 1166 敌兵布阵 单调更新区间查询和 #include <map> #include <set> #include <list> #include < ...
- KUANGBIN带你飞
KUANGBIN带你飞 全专题整理 https://www.cnblogs.com/slzk/articles/7402292.html 专题一 简单搜索 POJ 1321 棋盘问题 //201 ...
- [kuangbin带你飞]专题1-23题目清单总结
[kuangbin带你飞]专题1-23 专题一 简单搜索 POJ 1321 棋盘问题POJ 2251 Dungeon MasterPOJ 3278 Catch That CowPOJ 3279 Fli ...
- Tarjan 联通图 Kuangbin 带你飞 联通图题目及部分联通图题目
Tarjan算法就不说了 想学看这 https://www.byvoid.com/blog/scc-tarjan/ https://www.byvoid.com/blog/biconnect/ 下面是 ...
- 「kuangbin带你飞」专题二十 斜率DP
layout: post title: 「kuangbin带你飞」专题二十 斜率DP author: "luowentaoaa" catalog: true tags: mathj ...
- 「kuangbin带你飞」专题二十二 区间DP
layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...
- 「kuangbin带你飞」专题十九 矩阵
layout: post title: 「kuangbin带你飞」专题十九 矩阵 author: "luowentaoaa" catalog: true tags: mathjax ...
随机推荐
- 第二十篇 sys模块
修改环境变量 import sys sys.path.append() 但是,这种修复方式只是临时修改 如果要永久修改,就要电脑里配置环境变量. sys.argv:命令行参数List,第一个元素是程序 ...
- 树莓派搭建 Hexo 博客(二)
Hexo 一个开源的博客框架,本文记录了一下在树莓派上搭建 Hexo 博客的过程. 上一篇介绍了 Hexo 的配置,现在网站已经能在本地访问了,也能通过 hexo generate 命令生成静态界面 ...
- MyBatis整体了解
背景资料 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBati ...
- java long值转成时间格式
/** * 将long值转换为以小时计算的格式 * @param mss * @return */ public static String formatLongTime(long mss) { St ...
- PTA实验报告(循环 数组 函数)
一.循环PTA实验作业 题目一.7-2 求平方根序列前N项和 1.本题PTA提交列表 2.设计思路 本题调用了sqrt数学函数计算平方根,其次只用了一层循环,计算平方根之后使用循环累加计算总和sum. ...
- 算法(9)Find the Duplicate Number
一个数组中的长度是n+1,里面存放的数字大小的范围是[1,n],根据鸽巢原理,所以里面肯定有重复的数字,现在预定重复的数字就1个,让你找到这个数字! http://bookshadow.com/web ...
- jQuery添加、移除、改变class属性
jQuery中一般有3个关于改变元素class的函数addClass.removeClass.toggleClass addClass描述: 为每个匹配的元素添加指定的样式类名$('div').add ...
- well-known file is not secure
执行jstack pid时,提示well-known file is not secure. 原因是pid的启动用户不是当前用户,需要切换到启动用户下执行jstack即可. 可以通过如下命令来处理: ...
- hdu6097 Mindis(几何)
题解: 这里是用解析解的做法, 我们发现如果以P和Q做椭圆,那么当椭圆与圆相切的时候,答案最优 那么方程就是这样的 联立之后,解delta等于0,可以得到 答案就是2a了 注意不一定任何情况都有解,当 ...
- 【题解】NOIP2016换教室
哇好开心啊!写的时候真的全然对于这个加法没有把握,但还是大着胆子试着写了一下——竟然过了样例?于是又调了一下就过啦. 不过想想也觉得是正确的吧,互相独立的事件对于期望的影响自然也是相互独立的,可以把所 ...