• 带权并查集专题


    算是一个模板题吧

    给出[l,r]的区间和,相当于s[r]-s[l]

    一旦已经知道了 s[a]-s[b],s[b]-s[c],显然再给出一条[a,c]就可以判断"账本的真假"了

    将每条这样的信息(l,r,w),l,r放入一个集合中,

    用并查集来维护,并维护cha[l]=s[root]-s[l],cha[r]=s[root]-s[r]

    若 l,r已经在同一个集合中,就直接查询cha[l]-cha[r],判读与w是否相等

    合并操作是灵魂

    实话说如果没有对并查集合并操作和路径压缩理解够深刻 是不能写出来的

    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    const int maxn=505;
    int T;
    int fa[maxn],val[maxn];
    int find(int x){
    	if(fa[x]==x)return x;
    	else {
    		int oldfa=fa[x];
    		fa[x]=find(fa[x]);
    		val[x]+=val[oldfa];
    		return fa[x];
    	}
    }
    void solve();
    int main(){
    	cin>>T;
    	while(T--)solve();
         return 0;
    }
    void solve(){
    	int n,m;
    	cin>>n>>m;
    	for(int i=0;i<=n;i++)
    	fa[i]=i,val[i]=0;
        bool cmp=1;
    	while(m--){
    		int x,y,z;
    		cin>>x>>y>>z;
    		x--;
    		int fx=find(x),fy=find(y);
    		if(fx!=fy){
    			fa[fy]=fx;
    			val[fy]=val[x]-val[y]-z; 
    		}
    		else if(val[x]-val[y]!=z)cmp=0;
    	}
    	if(cmp)cout<<"true"<<endl;
    	else cout<<"false"<<endl;
    }
    

    每次给出两个昆虫的关系(异性关系),然后发现这些条件中是否有悖论

    和上一个题目差不多 维护x->rootx 的距离就好

    #include <cstdio>
    
    const int maxn = 2000 + 10;
    
    int pre[maxn];
    int r[maxn];  // 与根节点的关系,如果值为1则为异性如果为0则为同性
    
    int find(int x) {
        int t = pre[x];
        if (pre[x] != x) {
            pre[x] = find(pre[x]);
            r[x] = (r[x] + r[t]) % 2;  // 更新关系
        }
        return pre[x];
    }
    
    int main() {
       // freopen("input.txt", "r", stdin);
        int flag;
        int kase = 0;
        int T;
        int x, y;
        int n, m;
        scanf("%d", &T);
        while(T--) {
             flag = 1;
             scanf("%d%d", &n, &m);
             for(int i = 1; i <= n; i++) {
                 pre[i] = i;
                 r[i] = 0;
             }
             while(m--) {
                 scanf("%d%d", &x, &y);
                 if (!flag) continue;
                 int rx = find(x);
                 int ry = find(y);
                 if (rx == ry) {
                    if ((r[x] - r[y]+2) % 2 == 0) {
                        flag = 0;
                    }
                 }
                 else {
                     pre[ry] = rx;
                     r[ry] = (r[x] - r[y] + 1) % 2;
                 }
             }
             printf("Scenario #%d:\n",++kase);
             if(flag)
                    printf("No suspicious bugs found!\n\n");
             else
                    printf("Suspicious bugs found!\n\n");
        }
    
        return 0;
    }
    

    这个题是上面的题型的升级版 首先找到规律

    0吃1
    1吃2
    2吃0(2吃3)
    发现后面减去前面始终为1 所以就是 被吃 减 吃==1

    剩下的就差不太多了

    #include<bits/stdc++.h>
    using namespace std;
    #define lowbit(x) x&(-x)
    #define ll long long
    const int maxn=5e4+5;
    int n,m,ans; 
    int fa[maxn],val[maxn];
    int find(int x){
    	if(x==fa[x])return x;
    	int oldfa=fa[x];
    	fa[x]=find(fa[x]);
    	val[x]=(val[x]+val[oldfa])%3;
    	return fa[x];
    }
    int main(){
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)fa[i]=i,val[i]=0;
    	for(int i=1;i<=m;i++){
    		int c,x,y;
    		cin>>c>>x>>y;
    		if(x>n||y>n||(c==2&&x==y)){
    			ans++;continue;
    		}
    		int fx=find(x),fy=find(y);
    		if(c==1){
    			if(fx!=fy){
    			 fa[fy]=fx;
    			 val[fy]=(val[x]-val[y]+3)%3; 
    			}else if((val[y]-val[x]+3)%3)
    					ans++;
    		}else{
    			if(fx==fy){
    				if(((val[y]-val[x]+3)%3)!=1)
    				ans++;
    			}
    			else{
    				fa[fy]=fx;
    				val[fy]=(val[x]-val[y]+1+3)%3;
    			}
    		}
    	}
    	cout<<ans<<endl;
         return 0;
    }
    
    

    因为朋友之间是双向边 只要在合并的时候维护一下集合的最小值就好 最后每个集合只要保证有一个最小的人去贿赂就好

    思考:如果是单向边的话 那就要tarjan缩点 然后对每个缩完点入度为0的点中找出最小的一个 累加

  • 相关阅读:
    Linux权限
    Linux用户和用户组操作
    input输入框美化
    Ajax原理一篇就够了
    CSS样式----浮动(图文详解)
    linx系统操作
    文件打包,压缩,解包,解压缩
    Linux学习笔记(一)
    ios 11导航栏替换返回按钮图片,隐藏文字
    swift开发笔记23 BirthDays
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/16183814.html
Copyright © 2020-2023  润新知