带权并查集+二分图解法
带权并查集 分析A,B之间的相对距离,可以得到rnk[fa] = rnk[A]+x-rnk[B]。 注意到这时,对于原来的A的树,只更新了fa跟结点的权值, 那么其它结点的更新在查找的那一步里面实行了。
维护是相对距离
一开始 ab之间关系 a到b是s 在 fa fb 不一样是 我们可以当 a到fa b到fb 还有 a到b距离s 是向量 那么 rnk[fb]=rnk[a]-rnk[b]+s
种类并查集很多耶可以转到带权上orz
http://acm.hdu.edu.cn/showproblem.php?pid=3047
Zjnu Stadium HDU - 3047 带权并查集板子题
板子
#include <iostream> using namespace std; typedef long long ll; typedef pair<int,int> P; const int maxn=200000+10; ////const int INF = 0x3f3f3f3f; const int gxs = 3; int n,m; int pre[maxn],rnk[maxn]; void init() { for(int i=0; i<maxn; i++) pre[i]=i,rnk[i]=0; } //查找 int finds(int x){ if(x==pre[x]) return x; int t=pre[x]; pre[x]=finds(pre[x]); rnk[x]+=rnk[t]; return pre[x]; } //新建关系 void unions(int x,int y,int s) { int fx=finds(x),fy=finds(y); if (fx!=fy) { pre[fy]=fx; rnk[fy]=rnk[x]-rnk[y]+s;//+mod; // rnk[fy]%=mod; } } int main() { int b,a,k; ios::sync_with_stdio(false); while(cin>>n>>m){ init(); int ans=0; for(int i=0;i<m;i++){ cin>>a>>b>>k; if(finds(a)==finds(b)){ if(rnk[a]+k!=rnk[b]) ans++; }else{ unions(a,b,k); } } cout<<ans<<endl; } return 0; }
之后2题只是在带权并查集上 加了mod
就比如食物链 a吃b b吃c c吃a 一样 0 表示同类 1 表示x吃y 2表示y吃x
他们关系成环 所以知道 a b距离1 b c 距离又是 1 很显然 a c距离是2 这样 就是 c吃a 每次分析相对位置就好
#include <iostream> using namespace std; typedef long long ll; typedef pair<int,int> P; const int maxn=200000+10; ////const int INF = 0x3f3f3f3f; //const long long mod = 1e9+7; const int gxs = 3; int n,m; const int mod=3; int pre[maxn],rnk[maxn]; //查找 void init(){ for(int i=0;i<maxn;i++) pre[i]=i,rnk[i]=0; } int finds(int x) { if(x==pre[x]) return x; else { int root=finds(pre[x]); // 找到根节点 rnk[x]+=rnk[pre[x]]; // 权值合并,更新 rnk[x]%=mod; return pre[x]=root; // 压缩路径 } } //新建关系 void unions(int x,int y,int m){ int a=finds(x),b=finds(y); if(a!=b){ pre[b]=a; rnk[b]=rnk[x]+m-rnk[y]; rnk[b]+=gxs; rnk[b]%=gxs; } } int main() { int b,a,k; ios::sync_with_stdio(false); cin>>n>>m; init(); int ans=0; for(int i=0;i<m;i++){ cin>>k>>a>>b; if((k==2&&a==b)||a>n||b>n||a<=0||b<=0) { ans++; } else if(k==1){ if(finds(a)==finds(b)&&(rnk[a]!=rnk[b])) ans++; else unions(a,b,0); } else if(k==2){ if(finds(a)==finds(b)&&(rnk[a])!=(rnk[b]+2)%mod) ans++; else unions(a,b,1); } } cout<<ans<<endl; return 0; }
排序是必须的,我们要尽可能让危害大的罪犯在两个监狱里。
那么,再结合敌人的敌人和自己在一个监狱的规律合并。
当查找时发现其中两个罪犯不可避免地碰撞到一起时,只能将其输出并结束。
带权 mod取2;
wa 第一个点 就是没有冲突时输出 0 orz
#include <iostream> #include <vector> #include <algorithm> using namespace std; typedef long long ll; typedef pair<int,int> P; const int maxn=200000+10; ////const int INF = 0x3f3f3f3f; const int gxs = 2; const int mod=2; int n,m; int pre[maxn],rnk[maxn]; void init() { for(int i=0; i<maxn; i++) pre[i]=i,rnk[i]=0; } //查找 int finds(int x){ if(x==pre[x]) return x; int t=pre[x]; pre[x]=finds(pre[x]); rnk[x]+=rnk[t]; rnk[x]%=gxs; return pre[x]; } //新建关系 void unions(int x,int y,int s) { int fx=finds(x),fy=finds(y); if (fx!=fy) { pre[fy]=fx; rnk[fy]=rnk[x]-rnk[y]+s+gxs; rnk[fy]%=mod; } } struct node{ int a,b; ll val; bool operator < (const node &a)const{ return a.val<val; } }; vector<node> que; int main() { cin>>n>>m; int a,b; ll v; for(int i=0;i<m;i++){ cin>>a>>b>>v; que.push_back(node{a,b,v}); } sort(que.begin(),que.end()); init(); // ll ans=que[m-1].val; for(int i=0;i<m;i++){ a=que[i].a; b=que[i].b; v=que[i].val; if(finds(a)==finds(b)){ if(rnk[a]==rnk[b]){ cout<<v<<endl; return 0; } }else{ unions(a,b,1); } } cout<<0<<endl; return 0; }
接下来二分图解法 二分枚举最大边 看看能不能切二部份
#include <iostream> #include <vector> #include <algorithm> #include <cstring> #include <queue> using namespace std; typedef long long ll; typedef pair<int,int> P; const int maxn=20000+10; ////const int INF = 0x3f3f3f3f; const int gxs = 2; const int mod=2; int n,m; struct node { int to; ll val; }; vector<node> G[maxn]; int col[maxn]; void add_edge(int a,int b,ll v) { G[a].push_back(node {b,v}); G[b].push_back(node {a,v}); } int flag; bool ok(ll mid) { queue <int> q; for(int i=1; i<=n; i++) if(!col[i]) { q.push(i); col[i]=1; while(!q.empty()) { int x=q.front(); q.pop(); for(int i=0; i<G[x].size(); i++) if(G[x][i].val>=mid) {// 跟上面一样 不能二分图了 2人一个集合里面 出现的最小冲突 就是mid if(!col[G[x][i].to]) { q.push(G[x][i].to); if(col[x]==1) col[G[x][i].to]=2; else col[G[x][i].to]=1; } if(col[G[x][i].to]==col[x]) return false; } } } return true; } int main() { cin>>n>>m; int a,b; ll v; ll l,r; for(int i=0; i<m; i++) { cin>>a>>b>>v; add_edge(a,b,v); r=max(r,v); } r++; l=0; ll ans=r-1; while(r-l>1) { ll mid=(l+r)>>1; // flag=0; memset(col,0,(n+5)*sizeof(int)); if(ok(mid)) { r=mid; } else l=mid; } cout<<l; return 0; }