并查集
并查集
关键操作
1.预处理
for(int i=1;i<=m;i++) fa[i]=i;
2.路径压缩O(n)
int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
3.merge
int fx=find(a[i].x),fy=find(a[i].y);
fa[fx]=fy;
3.按秩合并O(nlogn)
秩的意思就是树的高度,按秩合并过后并查集的结构为树形结构,
程序自动分析
第一步当然是先离散化
然后对于“=”操作,我们直接merge
对于“≠”操作,我们check一下他们如果在一个联通块中就 return no,否则不动
然后小trick,我们可以按 e 排个序,让 “=” 操作先执行
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int T,n;
int fa[1000005];
struct node{
long long x,y;int z;
bool operator < (const node &x) const {
return z>x.z;
}
}a[1000005];
long long b[2000050];
int m;
int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
bool flag;
int main() {
T=read();
while(T--) {
n=read();m=0;flag=0;
for(int i=1,x,y,z;i<=n;i++) {
scanf("%lld%lld",&a[i].x,&a[i].y);a[i].z=read();
b[++m]=a[i].x;b[++m]=a[i].y;
}
sort(b+1,b+1+m);
m=unique(b+1,b+1+m)-b-1;
for(int i=1;i<=n;i++) {
a[i].x=lower_bound(b+1,b+1+m,a[i].x)-b;
a[i].y=lower_bound(b+1,b+1+m,a[i].y)-b;
}
sort(a+1,a+1+n);
for(int i=1;i<=m;i++) fa[i]=i;
for(int i=1;i<=n;i++) {
int fx=find(a[i].x),fy=find(a[i].y);
if(a[i].z==1) {
fa[fx]=fy;
} else {
if(fa[fx]==fa[fy]) {
flag=1;break;
}
}
}
if(flag) puts("NO");
else puts("YES");
}
return 0;
}
supermarket
简而言之,就是维护一个时间并查集,一开始的时间都是截止时间卖出,然后按利润从大到小排序(贪心),卖了这个就把其余的这天卖出的和前一天卖出的合并,然后每次 x=find(a[i].d)找到的都是能卖出的最靠后的天数
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,mxtime;
int fa[10005];
struct node{
int p,d;
bool operator < (const node &x) const {
return p>x.p;
}
}a[10005];
int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int ans;
int main() {
while(scanf("%d",&n)!=EOF) {
ans=0;mxtime=0;
for(int i=1;i<=n;i++) {
scanf("%d%d",&a[i].p,&a[i].d);
mxtime=max(mxtime,a[i].d);
}
for(int i=1;i<=mxtime;i++)
fa[i]=i;
sort(a+1,a+1+n);
for(int i=1;i<=n;i++) {
int x=find(a[i].d);
if(x>0) {
ans+=a[i].p;
fa[x]=x-1;
}
}
printf("%d\n",ans);
}
return 0;
}
[NOI2002]银河英雄传说
边带权并查集
维护siz和 d[]表示当前这个点到 fa[x] 的距离
然后每次合并注意先不要直接直接路径压缩fa
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n;
int fa[30005],d[30005],siz[30005];
int find(int x) {
if(x==fa[x]) return x;
int ans=find(fa[x]);
d[x]+=d[fa[x]];
return fa[x]=ans;
}
void merge(int x,int y) {
fa[x]=y;
d[x]+=siz[y];
siz[y]+=siz[x];
siz[x]=0;
}
int abs(int op) {
return op<0?-op:op;
}
int x,y;
char c;
int main() {
n=read();
for(int i=1;i<=30000;i++) fa[i]=i,siz[i]=1;
for(int i=1;i<=n;i++) {
cin>>c;
x=read();y=read();
int fx=find(x),fy=find(y);
if(c=='M') merge(fx,fy);
else {
if(fx!=fy) puts("-1");
else printf("%d\n",abs(d[y]-d[x])-1);
}
}
return 0;
}
Parity game
解答见蓝书p198
边带权并查集
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
const int N=40010;
int n,m;
struct node{
int l,r;int val;
}q[N];
int b[N],len;
int fa[N],d[N];
char c[5];
int find(int x) {
if(x==fa[x]) return x;
int ans=find(fa[x]);
d[x]^=d[fa[x]];
return fa[x]=ans;
}
int main() {
n=read();m=read();
for(int i=1;i<=m;i++) {
q[i].l=read();q[i].r=read();scanf("%s",c);
q[i].val=(c[0]=='o'?1:0);
b[++len]=q[i].l-1; b[++len]=q[i].r;
}
sort(b+1,b+1+len);
n=unique(b+1,b+1+len)-b-1;
for(int i=1;i<=2*n;i++) fa[i]=i;
for(int i=1;i<=m;i++) {
int x=lower_bound(b+1,b+1+n,q[i].l-1)-b-1;
int y=lower_bound(b+1,b+1+n,q[i].r)-b-1;
int fx=find(x),fy=find(y);
if(fx==fy) {
if((d[x]^d[y])!=q[i].val) {
printf("%d\n",i-1);
return 0;
}
} else {
fa[fx]=fy;
d[fx]=d[x]^d[y]^q[i].val;
}
}
printf("%d\n",m);
return 0;
}
扩展域并查集
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
const int N=40010;
int n,m;
struct node{
int l,r;int val;
}q[N];
int b[N],len;
int fa[N],d[N];
char c[5];
int find(int x) {
if(x==fa[x]) return x;
int ans=find(fa[x]);
d[x]^=d[fa[x]];
return fa[x]=ans;
}
int main() {
n=read();m=read();
for(int i=1;i<=m;i++) {
q[i].l=read();q[i].r=read();scanf("%s",c);
q[i].val=(c[0]=='o'?1:0);
b[++len]=q[i].l-1; b[++len]=q[i].r;
}
sort(b+1,b+1+len);
n=unique(b+1,b+1+len)-b-1;
for(int i=1;i<=2*n;i++) fa[i]=i;
for(int i=1;i<=m;i++) {
int x=lower_bound(b+1,b+1+n,q[i].l-1)-b;
int y=lower_bound(b+1,b+1+n,q[i].r)-b;
int x_odd=x,x_even=x+n;
int y_odd=y,y_even=y+n;
if(q[i].val==0) {
if(find(x_odd)==find(y_even)) {
printf("%d\n",i-1);
return 0;
}
fa[find(x_odd)]=find(y_odd);
fa[find(x_even)]=find(y_even);
} else {
if(find(x_odd)==find(y_odd)) {
printf("%d\n",i-1);
return 0;
}
fa[find(x_odd)]=find(y_even);
fa[find(x_even)]=find(y_odd);
}
}
printf("%d\n",m);
return 0;
}
食物链
扩展域并查集
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
const int N=150010;
int n,m,ans;
int fa[N];
char c[5];
int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main() {
n=read();m=read();
for(int i=1;i<=3*n;i++) fa[i]=i;
for(int i=1;i<=m;i++) {
int val=read(),x=read(),y=read();
if(x>n||y>n) {
ans++;
continue;
}
if(x==y&&val==2) {
ans++;
continue;
}
int x_self=x,x_enemy=x+n,x_eat=x+2*n;
int y_self=y,y_enemy=y+n,y_eat=y+2*n;
if(val==1) {
if(find(x_eat)==find(y_self) || find(y_eat)==find(x_self)) {
ans++;
continue;
}
fa[find(x_eat)]=find(y_eat);
fa[find(x_enemy)]=find(y_enemy);
fa[find(x_self)]=find(y_self);
} else {
if(find(y_eat)==find(x_self) || find(x_self)==find(y_self)) {
ans++;
continue;
}
fa[find(x_eat)]=find(y_self);
fa[find(x_self)]=find(y_enemy);
fa[find(x_enemy)]=find(y_eat);
}
}
printf("%d\n",ans);
return 0;
}
关押罪犯
排序,我们要尽可能让危害大的罪犯在两个监狱里。
那么,再结合敌人的敌人和自己在一个监狱的规律合并。
当查找时发现其中两个罪犯不可避免地碰撞到一起时,只能将其输出并结束。
还有一点很重要,就是没有冲突时一定输出0!!!
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,m;
int fa[100005],enemy[100005];
struct node{
int x,y,z;
bool operator < (const node &x) const {
return z>x.z;
}
}a[100005];
int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void merge(int x,int y) {
x=find(x),y=find(y);
fa[x]=y;
}
int main() {
n=read();m=read();
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++) {
a[i].x=read();a[i].y=read();a[i].z=read();
}
sort(a+1,a+1+m);
for(int i=1;i<=m;i++) {
if(find(a[i].x)==find(a[i].y)) {printf("%d\n",a[i].z);return 0;}
if(!enemy[a[i].x]) enemy[a[i].x]=a[i].y;
else merge(enemy[a[i].x],a[i].y);
if(!enemy[a[i].y]) enemy[a[i].y]=a[i].x;
else merge(enemy[a[i].y],a[i].x);
}
puts("0");
return 0;
}
CF85E Guard Towers
排个序,从大到小
和上题类似,连自己和对方的敌人,直到两人或敌人的find相同,这是ans=i ;
然后再求连通块数目得到方案数
#include <cstdio>
#include <vector>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
#define pii pair<int,int>
#define MP make_pair
using namespace std;
const int N=100005;
const int P=1e9+7;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
inline void Max(int &x,int y){if(x<y)x=y;}
inline void Min(int &x,int y){if(x>y)x=y;}
int n,x[N],y[N];
long long ans;
vector< pii >v[N];
int fa[N];
inline int find(int x) {
return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline int jue(int x) {return x>0?x:(-x);}
inline int dist(int i,int j) {
return jue(x[i]-x[j])+jue(y[i]-y[j]);
}
int main() {
n=read();
for(int i=1;i<=n;i++) {
x[i]=read();y[i]=read();
for(int j=1;j<i;j++)
v[dist(i,j)].push_back(MP(i,j));
}
for(int i=1;i<=2*n;i++) fa[i]=i;
for(int i=10000;i>=0;i--) {
for(auto t: v[i]) {
int x=find(t.first),y=find(t.second);
int ex=find(t.first+n),ey=find(t.second+n);
if(x==y||ex==ey) {
ans=i;break;
}
fa[x]=ey;fa[y]=ex;
}
if(ans) break;
}
printf("%lld\n",ans);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=ans+1;i<=10000;i++) {
for(auto t: v[i]) {
int x=find(t.first),y=find(t.second);
fa[x]=y;
}
}
ans=1;
for(int i=1;i<=n;i++)
if(fa[i]==i)
ans=ans*2%P;
printf("%lld\n",ans);
return 0;
}
UVA 11354 - Bond
#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e5+5;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct Edge{
int u,v,w;
bool operator < (const Edge &x) const {
return w<x.w;
}
}e[N];
int hd[N],tot;
int n,m,fa[N],siz[N],val[N];
int find(int x) {
return x==fa[x]?x:find(fa[x]);
}
void merge(int x,int y,int z) {
x=find(x),y=find(y);
if(x==y) return;
if(siz[x]<siz[y]) swap(x,y);
fa[y]=x;val[y]=z;
if(siz[x]==siz[y]) siz[x]++;
}
int v[N];
int query(int x,int y) {
for(int i=0;i<=n;i++) v[i]=0;
int ans=1,ans1=0;
while(1) {
v[x]=ans;
if(x==fa[x]) break;
ans=max(ans,val[x]);
x=fa[x];
}
while(1) {
if(v[y]) {ans1=max(ans1,v[y]);break;}
if(y==fa[y]) break;
ans1=max(ans1,val[y]);
y=fa[y];
}
return ans1;
}
int cs;
int main() {
while(scanf("%d%d",&n,&m)==2) {
if(cs++) putchar('\n');
for(int i=0;i<=n;i++) fa[i]=i,siz[i]=0,val[i]=0;
for(int i=1;i<=m;i++) {
e[i].u=read(),e[i].v=read(),e[i].w=read();
}
sort(e+1,e+1+m);
for(int i=1;i<=m;i++)
merge(e[i].u,e[i].v,e[i].w);
int q;
q=read();
int x,y;
while(q--) {
x=read(),y=read();
printf("%d\n",query(x,y));
}
}
return 0;
}
货车运输
#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=2e5+5;
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct Edge{
int u,v,w;
bool operator < (const Edge &x) const {
return w>x.w;
}
}e[N];
int hd[N],tot;
int n,m,fa[N],siz[N],val[N],dep[N];
int find(int x) {
if(x==fa[x]) return val[x]=0,x;
int dad=find(fa[x]);
dep[x]=dep[fa[x]]+1;
return dad;
}
void merge(int x,int y,int z) {
x=find(x),y=find(y);
if(x==y) return;
if(siz[x]<siz[y]) swap(x,y);
fa[y]=x;val[y]=z;
if(siz[x]==siz[y]) siz[x]++;
}
int query(int x,int y) {
if(find(x)!=find(y)) return -1;
int ans=0x3f3f3f3f;
while(x!=y) {
if(dep[x]<dep[y]) swap(x,y);
ans=min(ans,val[x]);
x=fa[x];
}
return ans;
}
int cs;
int main() {
n=read();m=read();
for(int i=0;i<=n;i++) fa[i]=i,siz[i]=0,val[i]=0;
for(int i=1;i<=m;i++) {
e[i].u=read(),e[i].v=read(),e[i].w=read();
}
sort(e+1,e+1+m);
for(int i=1;i<=m;i++)
merge(e[i].u,e[i].v,e[i].w);
int q;
q=read();
int x,y;
while(q--) {
x=read(),y=read();
printf("%d\n",query(x,y));
}
return 0;
}
城市猎人
提交 http://192.168.14.142/problem/57
对于第i天 ,设x=m-i+1 ,则会有 $$ x1,x2...x*j $$这些边相连,且边权为 i ,如果做过按秩合并的题,那么可一眼看出。。。接下来就是按秩合并咯
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=100010;
inline int read() {
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x;
}
int n,m,q;
int fa[N],siz[N],dep[N];
int val[N];
inline int find(int x) {
if(fa[x]==x) return dep[x]=0,x;
int dad=find(fa[x]);
dep[x]=dep[fa[x]]+1;
return dad;
}
void merge(int x,int y,int z) {
x=find(x),y=find(y);
if(x!=y) {
if(siz[x]<siz[y]) swap(x,y);
fa[y]=x;val[y]=z;
if(siz[x]==siz[y]) siz[x]++;
}
}
int main() {
n=read();m=read();q=read();
for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
for(int i=1;i<=m;i++) {
int x=m-i+1;
for(int j=1;x*j+x<=n;j++) merge(x*j,x*j+x,i);
}
while(q--) {
int x=read(),y=read(),ans=0;
find(x),find(y);
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y]) ans=max(ans,val[x]),x=fa[x];
while(x!=y) {
ans=max(ans,val[x]);x=fa[x];
ans=max(ans,val[y]);y=fa[y];
}
printf("%d\n",ans);
}
return 0;
}