带权并查集
就是合并的时候加个权值,找祖父时加个权值。
对于闭区间的时候一定要变成开区间 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。
代码:

#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); } }
题目:相见减做判断, 找规律,本身就是开区间

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
代码:

#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); }
1