目录
P3144 [USACO16OPEN]Closing the Farm S
P6121 [USACO16OPEN]Closing the Farm G
P5092 [USACO04OPEN]Cube Stacking (带权并查集)
P2949 [USACO09OPEN]Work Scheduling G
P2814 家谱
#include <bits/stdc++.h>
using namespace std;
int len, cnt, father, fa[50010];
string s;
map<string, int> m;
string a[50010];
int find(int x)
{
if(fa[x]==x){
return x;
}
else return fa[x]=find(fa[x]);
}
int main()
{
while(cin>>s && s[0]!='$'){
string name="";
len=s.length();
for(int i=1; i<len; ++i){
name+=s[i];
}
if(s[0]=='#'){
if(m.count(name)!=0){
father=m[name];
continue;
}
cnt++;
m[name]=cnt;
a[cnt]=name;
fa[cnt]=cnt;
father=cnt;
}
else if(s[0]=='+'){
if(m.count(name)!=0){
int id=m[name];
fa[id]=father;
continue;
}
cnt++;
m[name]=cnt;
a[cnt]=name;
fa[cnt]=father;
}
else if(s[0]=='?'){
int id=m[name];
cout << name << " " << a[find(id)] << endl;
}
}
return 0;
}
P1955 [NOI2015] 程序自动分析
并查集和map(哈希)结合起来的一道好题
#include <bits/stdc++.h>
using namespace std;
int t, n, fa[1000010], cnt;
bool flag;
map<int, int> m;
struct node
{
int x, y, z;
}limit[1000010];
bool cmp(node a, node b)
{
return a.z>b.z;
}
int find(int x)
{
if(fa[x]==x){
return x;
}
return fa[x]=find(fa[x]);
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
if(fu!=fv){
fa[fu]=fv;
}
}
int main()
{
scanf("%d", &t);
while(t--){ //10
scanf("%d", &n);
m.clear();
flag=true;
for(int i=1; i<=n; ++i){ //1e6
scanf("%d %d %d", &limit[i].x, &limit[i].y, &limit[i].z);
}
sort(limit+1, limit+n+1, cmp); //1e6*20
for(int i=1; i<=n; ++i){ //1e6
int u=limit[i].x;
int v=limit[i].y;
int e=limit[i].z;
if(m.count(u)==0){
cnt++;
fa[cnt]=cnt; //初始化并查集
m[u]=cnt; //将大范围的整数u映射成小范围的整数cnt
}
if(m.count(v)==0){
cnt++;
fa[cnt]=cnt; //初始化并查集
m[v]=cnt; //将大范围的整数v映射成小范围的整数cnt
}
if(e==1){ //合并
merge(m[u], m[v]);
}
else{ //判断u和v的祖先是否相同
int fu=find(m[u]);
int fv=find(m[v]);
if(fu==fv){
flag=false;
printf("NO\n");
break;
}
}
}
if(flag){
printf("YES\n");
}
}
return 0;
}
P2256 一中校运会之百米跑 (并查集结合map)
#include <bits/stdc++.h>
using namespace std;
const int N=20010;
int n, m, k, fa[N], u, v, fu, fv;
string name1, name2;
map<string, int> asd;
int find(int x)
{
if(fa[x]==x){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
fa[fu]=fv;
}
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
fa[i]=i;
cin >> name1;
asd[name1]=i;
}
while(m--){
cin >> name1 >> name2;
u=asd[name1];
v=asd[name2];
merge(u, v);
}
scanf("%d", &k);
while(k--){
cin >> name1 >> name2;
u=asd[name1];
v=asd[name2];
if(find(u)==find(v)){
printf("Yes.\n");
}
else{
printf("No.\n");
}
}
return 0;
}
P3144 [USACO16OPEN]Closing the Farm S
并查集倒着做合并, AC
#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, close[3010], fa[3010], cnt;
bool in[3010], vis[3010];
string ans[3010];
vector<int> a[3010];
int find(int x)
{
if(x==fa[x]){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
fa[fu]=fv;
}
int main()
{
scanf("%d %d", &n, &m);
while(m--){
scanf("%d %d", &u, &v);
a[u].push_back(v);
a[v].push_back(u);
}
for(int i=1; i<=n; ++i){
fa[i]=i;
scanf("%d", &close[i]); //要关掉的谷仓编号
}
for(int i=n; i>=1; --i){
u=close[i];
in[u]=true; //该点加入到图中
for(int j=0; j<a[u].size(); ++j){
v=a[u][j];
if(in[v]){
merge(u, v);
}
}
memset(vis, 0, sizeof(vis));
cnt=0;
for(int j=1; j<=n; ++j){
if(in[j] && !vis[find(j)]){
vis[find(j)]=true;
cnt++;
if(cnt>1){
break;
}
}
}
if(cnt>1){
ans[i]="NO";
}
else{
ans[i]="YES";
}
}
for(int i=1; i<=n; ++i){
cout << ans[i] << endl;
}
return 0;
}
暴力做法,50分, TLE
#include <bits/stdc++.h>
using namespace std;
int n, m, a[3010][3010], u, v, start[3010], close, vis[3010], cnt;
queue<int> q;
bool check(int x)
{
//多测清空
cnt=0;
memset(vis, 0, sizeof(vis));
while(!q.empty()){
q.pop();
}
//找起点
for(int i=1; i<=n; ++i){
if(start[i]==false){
q.push(i);
vis[i]=true;
cnt=1;
break;
}
}
//遍历
while(!q.empty()){
if(cnt==n-x){
return true;
}
u=q.front();
q.pop();
for(int i=1; i<=n; ++i){
if(a[u][i] && !vis[i]){
vis[i]=true;
cnt++;
q.push(i);
}
}
}
return false;
}
int main()
{
scanf("%d %d", &n, &m);
while(m--){
scanf("%d %d", &u, &v);
a[u][v]=a[v][u]=1; //建边
}
for(int i=1; i<=n; ++i){
if(check(i-1)){ //关掉i-1个谷仓
printf("YES\n");
}
else{
printf("NO\n");
}
scanf("%d", &close); //要关掉的谷仓编号
start[close]=true; //标记为关闭状态
for(int j=1; j<=n; ++j){ //断边
a[close][j]=a[j][close]=0;
}
}
return 0;
}
P6121 [USACO16OPEN]Closing the Farm G
#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, close[200010], fa[200010], cntedge, cntnode;
bool in[200010], vis[200010];
string ans[200010];
vector<int> a[200010];
int find(int x)
{
if(x==fa[x]){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
fa[fu]=fv;
}
int main()
{
scanf("%d %d", &n, &m);
while(m--){
scanf("%d %d", &u, &v);
a[u].push_back(v);
a[v].push_back(u);
}
for(int i=1; i<=n; ++i){
fa[i]=i;
scanf("%d", &close[i]); //要关掉的谷仓编号
}
//O(M)
for(int i=n; i>=1; --i){
u=close[i];
cntnode++;
in[u]=true; //该点加入到图中
for(int j=0; j<a[u].size(); ++j){
v=a[u][j];
if(in[v] && find(u)!=find(v)){
merge(u, v);
cntedge++;
}
}
if(cntedge+1<cntnode){
ans[i]="NO";
}
else{
ans[i]="YES";
}
}
for(int i=1; i<=n; ++i){
cout << ans[i] << endl;
}
return 0;
}
P1197 [JSOI2008] 星球大战
#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, k, close[400010], fa[400010], cntedge, cntnode, asd;
int ans[400010];
bool in[400010], vis[400010];
vector<int> a[400010];
int find(int x)
{
if(x==fa[x]){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
fa[fu]=fv;
}
int main()
{
scanf("%d %d", &n, &m);
for(int i=0; i<n; ++i){
fa[i]=i;
}
while(m--){
scanf("%d %d", &u, &v);
a[u].push_back(v);
a[v].push_back(u);
}
scanf("%d", &k);
for(int i=1; i<=k; ++i){
scanf("%d", &close[i]); //要关掉的星球编号
vis[close[i]]=true; //如果某个星球始终没有被关掉, 则vis为false
}
asd=n-k; //假设这些点都没相连, 联通块数量最多为n-k
//逆向思维处理, 反向建设相当于正向摧毁
//先处理k座星球全部被摧毁后的情况
for(int i=0; i<n; ++i){
if(!vis[i]){ //i始终没有被摧毁
in[i]=true; //i在图里
for(int j=0; j<a[i].size(); ++j){
v=a[i][j]; //枚举与i相连的点
if(!vis[v] && find(i)!=find(v)){ //如果该点也始终没有被摧毁, 并且有边相连
merge(i, v); //合并
asd--; //联通块数量减一
}
}
}
}
ans[k+1]=asd;
//再逆向处理第i座星球被摧毁前的情况
for(int i=k; i>=1; --i){
//把第i座星球加入到图中
u=close[i];
asd++;
in[u]=true; //该点加入到图中
for(int j=0; j<a[u].size(); ++j){
v=a[u][j];
if(in[v] && find(u)!=find(v)){ //连一条边, 没有形成环
merge(u, v); //合并
asd--; //联通块数量--
}
}
ans[i]=asd;
}
for(int i=1; i<=k+1; ++i){
cout << ans[i] << endl;
}
return 0;
}
P5092 [USACO04OPEN]Cube Stacking (带权并查集)
#include <bits/stdc++.h>
using namespace std;
const int N=30010;
int p, fa[N], dis[N], size[N], u, v;
char opt;
int find(int x)
{
if(fa[x]==x){
return x;
}
int root=find(fa[x]); //找到自己祖宗结点的祖宗
dis[x]+=dis[fa[x]]; //更新到自己祖宗的祖宗(root)的距离, 等于自己到当前祖宗结点的距离+当前祖宗结点到它的祖宗结点的距离
return fa[x]=root; //直接链到最终的祖宗
}
void merge(int u, int v)
{
int fu, fv;
fu=find(u);
fv=find(v);
if(fu==fv) return;
fa[fu]=fv;
dis[fu]+=size[fv]; //距离先存在祖宗结点fu这里, 后面需要用的时候find去更新
size[fv]+=size[fu]; //祖宗保存元素个数
}
int main()
{
scanf("%d", &p);
for(int i=1; i<=N; ++i){ //并查集初始化, dis[i]默认为0
fa[i]=i;
size[i]=1;
}
while(p--){
cin >> opt;
if(opt=='M'){ //将包含 u 的立方柱移动到包含 v 的立方柱上
scanf("%d %d", &u, &v);
merge(u, v);
}
else if(opt=='C'){ //统计含 u 的立方柱中,在 u 下方的方块数目。
scanf("%d", &u);
find(u);
printf("%d\n", dis[u]);
}
}
return 0;
}
P5877 棋盘游戏
二维转一维,用并查集处理
#include <bits/stdc++.h>
using namespace std;
int n, m, c, x, y, a[510][510], fa[250010], id, asd;
int mx[5]={0, -1, 1, 0, 0};
int my[5]={0, 0, 0, -1, 1};
int find(int x)
{
if(fa[x]==x){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
fa[fu]=fv;
}
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
for(int j=1; j<=n; ++j){
id=(i-1)*n+j;
fa[id]=id;
}
}
memset(a, -1, sizeof(a));
while(m--){
scanf("%d %d %d", &c, &x, &y);
asd++;
id=(x-1)*n+y;
a[x][y]=c;
for(int i=1; i<=4; ++i){
int nx=x+mx[i];
int ny=y+my[i];
if(nx>=1 && nx<=n && ny>=1 && ny<=n && a[nx][ny]==c && find(id)!=find((nx-1)*n+ny)){
merge(id, (nx-1)*n+ny);
asd--;
}
}
printf("%d\n", asd);
}
return 0;
}
P8686 [蓝桥杯 2019 省 A] 修改数组
路径压缩,删除中间的数,相当于区间合并
#include <bits/stdc++.h>
using namespace std;
int n, fa[2000010], a[100010];
bool vis[2000010];
int find(int x)
{
if(fa[x]==x){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
fa[fu]=fv;
}
int main()
{
scanf("%d", &n);
for(int i=1; i<=2000000; ++i){
fa[i]=i;
}
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
}
for(int i=1; i<=n; ++i){
if(find(a[i])==a[i]){ //没出现
cout << a[i] << " ";
merge(a[i], a[i]+1);
}
else{ //出现过
cout << fa[a[i]] << " ";
merge(fa[a[i]], fa[a[i]]+1);
}
}
return 0;
}
P1840 Color the Axis
并查集进行区间合并
#include <bits/stdc++.h>
using namespace std;
int n, m, fa[200010], l, r, asd;
int find(int x)
{
if(x==fa[x]){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
//让大的当父亲, 不然会TLE
fa[fu]=fv;
}
int main()
{
scanf("%d %d", &n, &m);
asd=n;
//注意细节, 需要初始化到n+1, 不然TLE
for(int i=1; i<=n+1; ++i){
fa[i]=i;
}
while(m--){
scanf("%d %d", &l, &r);
for(int i=l; i<=r; i=find(i)){
//如果该点没有被涂过
if(find(i)==i){
asd--; //答案--
//该点被涂过了, 将该点和下一个点合并,
//当下次再找到该点时, 直接通过find(i)跳到下一个没有被涂的点
merge(i, i+1);
}
}
printf("%d\n", asd);
}
return 0;
}