带权并查集

带权并查集 

  就是合并的时候加个权值,找祖父时加个权值。

  对于闭区间的时候一定要变成开区间 a-1  ->B   很关键  下面 一定要 a--;将这个东西给连接起来。( 】这个样子

题型:

  针对性的解决 一个很长的区间问题。 给出很多的小区间 ,来判断 这些小区间很前面的小区间冲不冲突。

当元素太大的时候可以理想化。

  特别注意数组的超限问题 超过多少就没了。 

  利用map 《int,int》 等等的 一一对应表明。 就不会出现内存问题了,这个是动态的。// 栗子 奇偶的那道题。

   预处理: for(ri i=1;i<=n;i++)fa[i]=i;

  小注意 是 val【a】+=val【t】 这个t之前父亲的。 而且是+;

   找祖父

int find(int a)
{
    if(a!=fa[a])
    {
        int t=fa[a];
        fa[a]=find(fa[a]);
        val[a]+=val[t]; // 跟新 这个节点到 祖父的 权值 
    }
    return fa[a];
}

  合并

    int l=find(a);
            int r=find(b);
            if(l!=r)
            {
                fa[l]=r;
                val[l]=-val[a]+c+val[b];   //跟新权值 
            }
            else
            {
                if(val[a]-val[b]!=c) ans++;// 解决问题 小的节点到 祖父的权值 大 
            }
        }

例题:

 

HDU-3038-How Many Answers Are Wrong

这个题题目的废话比较多,这里简述一下大意:有M个数,不知道它们具体的值,但是知道某两个数之间(包括这两个数)的所有数之和,现在给出N个这样的区间和信息,需要判断有多少个这样的区间和与前边已知的区间和存在矛盾。例如给出区间和[1,4]为20,[3,4]为15,再给出[1,2]为30,显然这个[1,2]的值就有问题,它应该为20-15=5
View Code

  代码:

#include <bits/stdc++.h>
using namespace std;
#define ri register int 
const int N=202000;
const int M=40000;
int n,m;
int val[N],fa[N],ans;
int find(int a)
{
    if(a!=fa[a])
    {
        int t=fa[a];
        fa[a]=find(fa[a]);
        val[a]+=val[t]; // 跟新 这个节点到 祖父的 权值 
    }
    return fa[a];
}
int main(){
    while(~scanf("%d%d",&n,&m))
    {
        ans=0;memset(val,0,sizeof val);
        for(ri i=0;i<=n;i++) fa[i]=i;
        for(ri i=1;i<=m;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            a--;
            int l=find(a);
            int r=find(b);
            if(l!=r)
            {
                fa[l]=r;
                val[l]=-val[a]+c+val[b];   //跟新权值 
            }
            else
            {
                if(val[a]-val[b]!=c) ans++;// 解决问题 小的节点到 祖父的权值 大 
            }
        }
        printf("%d
",ans);
    }
}
View Code

题目:相见减做判断, 找规律,本身就是开区间

poj-1182-食物链

 

食物链

Time Limit: 1000MS         Memory Limit: 10000K
Total Submissions: 92490         Accepted: 27910
Description

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。 
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。 
有人用两种说法对这N个动物所构成的食物链关系进行描述: 
第一种说法是"1 X Y",表示X和Y是同类。 
第二种说法是"2 X Y",表示X吃Y。 
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。 
1) 当前的话与前面的某些真的话冲突,就是假话; 
2) 当前的话中X或Y比N大,就是假话; 
3) 当前的话表示X吃X,就是假话。 
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。 

Input

第一行是两个整数N和K,以一个空格分隔。 
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。 
若D=1,则表示X和Y是同类。 
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5
Sample Output

3
View Code

代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
const int M = 10005;
const int N = 50050;
#define ri register int 
int fa[N],val[N];
int find(int a)
{
    if(a!=fa[a])
    {
        int t=fa[a];
        fa[a]=find(fa[a]);
        val[a]=(val[a]+val[t])%3;
    }
    return fa[a];
}
int n,m,ans;
int main(){
    scanf("%d%d",&n,&m);
    for(ri i=1;i<=n;i++) fa[i]=i;
    for(ri i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&c,&a,&b);
        if(a>n||b>n||(a==b&&c==2)) 
        { 
         ans++;continue;
        }
        int l=find(a);
        int r=find(b);
        if(l!=r)
        {
            fa[l]=r;
            val[l]=(val[b]+c-1-val[a]+3)%3;
        }
        else{
            if((val[a]-val[b]+3)%3!=(c-1)) ans++;
        }
    }
    printf("%d
",ans);
}  
View Code

1

原文地址:https://www.cnblogs.com/Lamboofhome/p/11765200.html