把最近碰到的几个并查集题目综合一下。
并查集来做,我们可以把一件装备看成一条边,两个属性看成两个点,那么这就相当于读入了一张图
当读入每一个x,y时,我们找到两个点的祖先节点,fx,fy,我们保证祖先节点在该连通块
中编号(装备属性)最大,用vis数组记录能否用属性I攻击boss,那么两种情况
fx == fy,
说明此时已经构成环了,环上的每个节点都可以被选。
fx!=fy
说明在两个联通分量。(设fx,fy为两个联通分量的父亲节点,假设fx<fy)
两棵树,那只要把两棵树的父亲节点比较,最小的那个父亲节点可以被选定了,即pre[fx] = fy.
两个环,两个环上的所有点都已经被选过了,新边不影响。
一个环,一棵树,比较父亲节点的关系,来标记。别忘了把环的特性上传。
最后扫描所有特性来确定。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6+5; typedef long long ll; int flag[maxn]; int vis[maxn]; int pre[maxn]; void init() { memset(flag,0,sizeof(flag)); memset(vis,0,sizeof(vis)); for(int i=1;i<maxn;i++) { pre[i] = i; } } int find1(int x) { int r = pre[x]; while(pre[r]!=r) { r = pre[r]; } int i = x,j; while(pre[i]!=r) { j = pre[i]; pre[i] = r; i = j; } return r; } void merge1(int x,int y) { int fx = find1(x); int fy = find1(y); if(fx==fy) { vis[fx] = 1; ///标记是否可以用该点 flag[fx] = 1; ///标记是否是环 } else { if(flag[fx]==0&&flag[fy]==0) { ///将树上小的节点标记可用 if(fx<fy) vis[fx] = 1,pre[fx] = fy; else vis[fy] = 1,pre[fy] = fx; } else if(flag[fx]==1&&flag[fy]==0) { if(fx>fy) vis[fy] = 1,pre[fy] = fx; ///fx为环,fy小,将fy连到fx上,同时标记fy else vis[fy] = 1,pre[fx] = fy,flag[fy] = 1;///fx为环,fy大,将fx连到fy上 ///,同时标记fy并传递环特性 } else if(flag[fy]==1&&flag[fx]==0)///fy为环 { if(fx<fy) vis[fx] = 1,pre[fx] = fy; ///fx小,将fx连到fy上,同时标记fx else vis[fx] = 1,pre[fy] = fx,flag[fx] = 1;///fx大,将fy连到fx上,同时标记fx并传递环特性 } else { if(fx>fy) pre[fy] = fx; else pre[fx] = fy; } } } int main() { int n; scanf("%d",&n); init(); for(int i=1;i<=n;i++) { int x,y; scanf("%d %d",&x,&y); merge1(x,y); } for(int i=1;i<maxn;i++) { if(vis[i]) continue; else { cout<<(i-1)<<endl; return 0; } } return 0; }
E. Points, Lines and Ready-made Titles
这道题和上面方法一样,我只多了个离散化。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 1e5+5; vector<int> x; vector<int> y; int xx[maxn],yy[maxn]; int pre[maxn*2]; int flag[maxn*2]; int node[maxn*2]; int find1(int x) { int r = x; while(pre[r]!=r) { r = pre[r]; } int i = x,j; while(pre[i]!=r) { j = pre[i]; pre[i] = r; i = j; } return r; } void merge1(int x,int y) { x = find1(x),y = find1(y); if(x==y) { flag[y] = 1; ///标记为环 } else { if(flag[x]==0&&flag[y]==0) ///两个都是树 { if(x<y) pre[x] = y,node[y] += node[x]; ///把节点个数算到y上 else pre[y] = x,node[x] += node[y]; ///算到x上 } else if(flag[x]==1&&flag[y]==0)///x是环 { if(x<y) pre[x] = y,node[y] += node[x],flag[y] = 1;///y也标记为环 else pre[y] = x,node[x] += node[y]; } else if(flag[y]==1&&flag[x]==0)///y是环 { if(y<x) pre[y] = x,node[x] += node[y],flag[x] = 1;///x也标记为环 else pre[x] = y,node[y] += node[x]; } else { if(y<x) pre[y] = x,node[x] += node[y]; else pre[x] = y,node[y] += node[x]; } } } const ll mod = 1e9+7; ll quick_pow(ll a,ll n) { ll ret = 1; while(n) { if(n%2LL) ret = ret*a%mod; n /= 2LL; a = a*a%mod; } return ret; } int main() { int n; scanf("%d",&n); for(int i=0;i<maxn*2;i++) pre[i] = i; for(int i=0;i<maxn*2;i++) node[i] = 1,flag[i] = 0; ///每个连通分量初始值为1,环设为0 for(int i=1;i<=n;i++) { scanf("%d %d",&xx[i],&yy[i]); x.push_back(xx[i]); y.push_back(yy[i]); } sort(x.begin(),x.end()); ///unique和sort连用 x.erase(unique(x.begin(),x.end()),x.end()); sort(y.begin(),y.end()); y.erase(unique(y.begin(),y.end()),y.end()); for(int i=1;i<=n;i++) { int tempx = lower_bound(x.begin(),x.end(),xx[i])-x.begin(); int tempy = lower_bound(y.begin(),y.end(),yy[i])-y.begin(); tempy = 1e5+tempy; merge1(tempx,tempy); } ll sum = 1; for(int i=0;i<maxn*2;i++) { if(pre[i]==i) { if(flag[i]) sum = sum*quick_pow(2,node[i])%mod; else sum = sum*(quick_pow(2,node[i])-1)%mod; } } printf("%I64d ",sum); return 0; }
倒着并查集,代码复用的比较多。又臭又长。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e3+10; int vis[maxn][maxn]; int pre[maxn*maxn]; struct node { int x1,y1,x2,y2; }; node e[maxn*10]; int n,m,q; int check(int x,int y) { if(x<=0||x>n||y<=0||y>m) return 0; else if(!vis[x][y]) { return 1; } else return 0; } int cal(int i,int j) { return ((i-1)*m+j); } int find1(int x) { if(pre[x]==x) return x; else return pre[x] = find1(pre[x]); } int sum = 0; void merge1(int x,int y) { int fx = find1(x); int fy = find1(y); if(fx!=fy) { sum--; pre[fx] = fy; } } void fun(int i,int j) { if(!vis[i][j]) { if(check(i-1,j)) merge1(cal(i,j),cal(i-1,j)); if(check(i+1,j)) merge1(cal(i,j),cal(i+1,j)); if(check(i,j-1)) merge1(cal(i,j),cal(i,j-1)); if(check(i,j+1)) merge1(cal(i,j),cal(i,j+1)); } } int tmp[maxn*10]; int main() { scanf("%d %d %d",&n,&m,&q); memset(vis,0,sizeof(vis)); sum = n*m; for(int i=1;i<=q;i++) { scanf("%d %d %d %d",&e[i].x1,&e[i].y1,&e[i].x2,&e[i].y2); int x1,x2,y1,y2; x1 = e[i].x1,x2 = e[i].x2; if(x1>x2) swap(x1,x2); y1 = e[i].y1,y2 = e[i].y2; if(y1>y2) swap(y1,y2); if(x1==x2) { for(int j=y1;j<=y2;j++) { if(!vis[x1][j]) sum--; vis[x1][j]++; } } else { for(int j=x1;j<=x2;j++) { if(!vis[j][y1]) sum--; vis[j][y1]++; } } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { pre[(i-1)*m+j] = (i-1)*m+j; } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { fun(i,j); } } tmp[q+1]= sum; for(int i=q;i>=1;i--) { int x1,x2,y1,y2; x1 = e[i].x1,x2 = e[i].x2; if(x1>x2) swap(x1,x2); y1 = e[i].y1,y2 = e[i].y2; if(y1>y2) swap(y1,y2); if(x1==x2) { for(int j=y1;j<=y2;j++) ///黑色框本来 { vis[x1][j]--; if(check(x1,j)&&(pre[cal(x1,j)]==cal(x1,j))) { sum++; } } for(int j=y1;j<=y2;j++) { fun(x1,j); } } else { for(int j=x1;j<=x2;j++) { vis[j][y1]--; if(check(j,y1)&&(pre[cal(j,y1)]==cal(j,y1))) { sum++; } } for(int j=x1;j<=x2;j++) { fun(j,y1); } } tmp[i] = sum; } for(int i=2;i<=q+1;i++) { printf("%d ",tmp[i]); } return 0; }