算法总结—并查集2
直接看题目:
\text{\scriptsize{USACO}:\scriptsize{Closing\ the\ Farm\ S}}
银组题
这道题数据范围很小,每询问一次就跑一遍并查集,并统计一共有多少个不交集。时间复杂度
注意:
- 一个牧场关闭了就不可以再打开了。
- 每次询问都是在关闭牧场之前。
代码
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
struct node{
int u,v;
}a[3005];
int father[3005];
bool vis[3005];
bool cmp(node q,node h){
if(q.u==h.u){
return q.v<h.v;
}
return q.u<h.u;
}
int find1(int x){
if(father[x]==x){
return x;
}
father[x]=find1(father[x]);
return father[x];
}
void union1(int x,int y){
x=find1(x);
y=find1(y);
if(x!=y){
father[x]=y;
}
return;
}
bool check(int x){
for(int i=1;i<=n;i++){
father[i]=i;
}
for(int i=1;i<=m;i++){
if(vis[a[i].u]==1||vis[a[i].v]==1){
continue;
}
union1(a[i].u,a[i].v);
}
int cnt=0;
for(int i=1;i<=n;i++){
if(vis[i]){
continue;
}
if(father[i]==i){
cnt++;
}
}
return (cnt==1);
}
int main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a[i].u>>a[i].v;
if(a[i].u>a[i].v){
swap(a[i].u,a[i].v);
}
}
sort(a+1,a+1+m,cmp);
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(check(x)){
cout<<"YES\n";
}else{
cout<<"NO\n";
}
vis[x]=1;
}
return 0;
}
金组题
这道题
思路
我们设没有删点之前的序列为
不过添加点极限还是
代码
#include<iostream>
#include<vector>
using namespace std;
int n,m;
int father[200005];
vector<int>vec[200005];
int cnt=0;
string ans[200005];
int a[200005];
bool vis[200005];
int find1(int x){
if(father[x]==x){
return x;
}
father[x]=find1(father[x]);
return father[x];
}
void union1(int x,int y){
x=find1(x);
y=find1(y);
if(x!=y){
cnt--;
father[x]=y;
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
vec[u].push_back(v);
vec[v].push_back(u);
}
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
father[i]=i;
}
for(int i=n;i>=1;i--){
vis[a[i]]=1;
cnt++;
for(int j=0;j<vec[a[i]].size();j++){
if(vis[vec[a[i]][j]]==1){
union1(vec[a[i]][j],a[i]);
}
}
if(cnt==1){
ans[i]="YES";
}else{
ans[i]="NO";
}
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<"\n";
}
return 0;
}
奶酪
这道题就是将相交或相切的两个洞合并,看连着桌子的洞和奶酪顶部的洞是否属于同一个集合内。
- 两个洞的半径之和小于两球球心的欧几里得距离,则称这两个洞相离,也就是说这两个洞不能连在一起。
- 两个洞的半径之和等于两球球心的欧几里得距离,则称这两个洞相切,也就是说这两个洞正好能连在一起。
- 两个洞的半径之和大于两球球心的欧几里得距离,则称这两个洞相切,也就是说这两个洞能连在一起。
所以这道题我们只需要判断两个点之间是否相离或相切,如果是合并即可,不过初始化时,记得把
代码
#include<iostream>
#include<cmath>
using namespace std;
struct node{
double x,y,z;
}a[1005];
int father[1005];
int find1(int x){
if(father[x]==x){
return x;
}
father[x]=find1(father[x]);
return father[x];
}
void union1(int x,int y){
x=find1(x);
y=find1(y);
if(x!=y){
father[x]=y;
}
return;
}
double dis(double x_1,double x_2,double y_1,double y_2,double z_1,double z_2){
double x=(x_1-x_2)*(x_1-x_2);
double y=(y_1-y_2)*(y_1-y_2);
double z=(z_1-z_2)*(z_1-z_2);
return sqrt(x+y+z);
}
void work(){
double n,h,r;
cin>>n>>h>>r;
for(int i=0;i<=n+1;i++){
father[i]=i;
}
for(int i=1;i<=n;i++){
cin>>a[i].x>>a[i].y>>a[i].z;
if(a[i].z+r>=h){
union1(n+1,i);
}
if(a[i].z<=r){
union1(0,i);
}
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(dis(a[i].x,a[j].x,a[i].y,a[j].y,a[i].z,a[j].z)<=2.00*r){
union1(i,j);
}
}
}
if(find1(0)==find1(n+1)){
cout<<"Yes\n";
}else{
cout<<"No\n";
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int T;
cin>>T;
while(T--){
work();
}
return 0;
}
营救
二分答案最大拥挤度,每枚举一次,就重新跑一边并查集,并且判断
代码
#include<iostream>
#define int long long
using namespace std;
int n,m,s,t;
struct node{
int u,v,w;
}a[20005];
int father[10005];
int find1(int x){
if(father[x]==x){
return x;
}
father[x]=find1(father[x]);
return father[x];
}
void union1(int x,int y){
x=find1(x);
y=find1(y);
if(x!=y){
father[x]=y;
}
return;
}
bool check(int x){
for(int i=1;i<=n;i++){
father[i]=i;
}
for(int i=1;i<=m;i++){
if(a[i].w<=x){
union1(a[i].u,a[i].v);
}
}
return find1(s)==find1(t);
}
signed main(){
cin>>n>>m>>s>>t;
int l=0,r=-1e9;
for(int i=1;i<=m;i++){
cin>>a[i].u>>a[i].v>>a[i].w;
r=max(r,a[i].w);
}
r++;
while(l+1<r){
int mid=(l+r)/2;
if(check(mid)){
r=mid;
}else{
l=mid;
}
}
cout<<r;
return 0;
}