注意向量的方向:从根向x root->i=val[i]
hdu 1829
val[i]=0 表明与结点i与根结点同性 ,val[i]=1是异性
路径压缩的时候,x会挂到rootx下
路径压缩时:rootx->x=rootx->fa[x]+fa[x]->x 即 val[x]=(val[x]+val[fa[x]])%2;
合并时:y在的集合挂在x在的集合下 rootx->rooty=rootx->x+x->y+y->rooty 其中rootx->x==val[x], x->y==1(因为是异性),
y->rooty==rooty->y==val[y]
判断有同性恋时:val[x]==val[y]
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
//与根结点同性为0,异性为1
int fa[2010];
int val[2010];
int n,m;
int flag=0;
void init(int n)
{
flag=0;
for(int i=1;i<=n;++i)
{
fa[i]=i;
val[i]=0;
}
}
int find(int x)
{
if(x==fa[x])
return x;
else
{
int tmp=find(fa[x]);
val[x]=(val[x]+val[fa[x]])%2;
fa[x]=tmp;
return tmp;
}
}
void merge(int x,int y)
{
int fx=find(x);
int fy=find(y);
if(fx==fy)
{
if(val[x]==val[y]) //0+0 ||1+1 是为0 同性
{
flag=true;
}
return ;
}
else //将y挂在x下
{
fa[fy]=fx; // !!!脑子晕乎乎的写成fa[y]=fx;
val[fy]=(val[x]+1+val[y])%2; // !!!写成val[y]=(val[x]+1+val[y])%2;
}
}
int main()
{
int T;
scanf("%d",&T);
int kase=0;
while(T--)
{
scanf("%d%d",&n,&m);
init(n);
flag=0;
for(int i=1;i<=m;++i)
{
int x,y;
scanf("%d%d",&x,&y);
if(flag) continue;
merge(x,y);
}
if(flag)
printf("Scenario #%d:\nSuspicious bugs found!\n\n",++kase);
else
printf("Scenario #%d:\nNo suspicious bugs found!\n\n",++kase);
}
}
poj 1703与上代码基本一致(罪犯是否在同一个团队相当于是否为同性,假设根结点为龙队,是龙队的为0,不是龙队的为1)
其中还不能判断说明->还没有merge过->find(x)!=find(y)
poj 1182食物链
val[i]=0同类;val[i]=1,i被根结点吃;val[i]=2,i吃根结点
路径压缩时:rootx->x=rootx->fa[x]+fa[x]->x rootx->fa[x]==val[fa[x]], fa[x]->x==val[x] val[x]=(val[x]+val[fa[x]])%3;
合并时:y在的集合挂在x在的集合下 rootx->rooty=rootx->x+x->y+y->rooty 其中rootx->x==val[x], x->y==题目给出的同类或x吃y,如果是同类,则x->y值为0(d-1),如果是x吃y,则x->y的值为1(d-1)
y->rooty==3-rooty->y==3-val[y] [0,1,2] 反过来[3,2,1]在同余的情况下是一样的
判断有撒谎者时:
情况1:数字越界
情况2:同类相食
情况3:d=1 而不同类
d=2而不满足x吃y x=2时y=0,x=1时y=2(x被根结点吃,y吃根结点,则x吃y),x=0时y=1(x为根结点,y被根结点吃)
即(val[x]+1)%3==val[y] 时满足x吃掉y
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e4+10;
int fa[N];
int val[N];
int n,m;
//val[i]=0同类,val[i]=1,i被根结点吃;val[i]=2,i吃根结点
int ans=0;
void init(int n)
{
for(int i=1;i<=n;++i)
{
fa[i]=i;
val[i]=0;
}
}
int find(int x)
{
if(x==fa[x])
return x;
else
{
int tmp=find(fa[x]);
val[x]=(val[x]+val[fa[x]])%3;
fa[x]=tmp;
return tmp;
}
}
void merge(int x,int y,int d)
{
int fx=find(x);
int fy=find(y);
if(fx==fy)
{
if(d==1&&val[x]!=val[y]) ++ans;
else if(d==2&&(val[x]+1)%3!=val[y]) ++ans;
}
if(fx!=fy)
{
fa[fy]=fx;
val[fy]=(val[x]+d-1+3-val[y])%3;
}
}
int main()
{
scanf("%d%d",&n,&m);
init(n);
for(int i=1;i<=m;++i)
{
int d,x,y;
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n) {++ans; continue;} //保证同一条只记录一次
else if(d==2&&x==y) {++ans; continue;}
else
merge(x,y,d);
}
printf("%d\n",ans);
return 0;
}
hihocoder 1515 分数调查
root->i=val[i]代表i比root高多少
路径压缩和上面一样
合并: rootx->rooty=rootx->x+x->y+y->rooty rootx->x==val[x], x->y==-s(y比x高-s),y->rooty=-val[y]
#include<stdio.h>
const int N=1e5+10;
int fa[N];
int val[N];
int n,m,q;
//val[i]记录比根结点高的分数
int ans=0;
void init(int n)
{
for(int i=1;i<=n;++i)
{
fa[i]=i;
val[i]=0;
}
}
int Find(int x)
{
if(x==fa[x])
return x;
else
{
int tmp=Find(fa[x]);
val[x]=(val[x]+val[fa[x]]);
fa[x]=tmp;
return tmp;
}
}
void Merge(int x,int y,int s)
{
int fx=Find(x);
int fy=Find(y);
if(fx!=fy)
{
fa[fy]=fx;
val[fy]=val[x]-s-val[y];
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
init(n);
for(int i=1;i<=m;++i)
{
int x,y,s;
scanf("%d%d%d",&x,&y,&s);
Merge(x,y,s);
}
for(int i=1;i<=q;++i)
{
int x,y;
scanf("%d%d",&x,&y);
int fx=Find(x);
int fy=Find(y);
if(fx==fy)
printf("%d\n",val[x]-val[y]);
else
printf("-1\n");
}
return 0;
}
hdu 3047 Zjnu Stadium
题意:B必须坐在A顺时针X位置处
root->i=val[i] ( i相对于根顺时针偏离的距离 )
合并:rootx->rooty=rootx->x+x->y+y->rooty=val[x]+s-val[y] (x->y是y比照root->i,是y相对于x顺时针多少,即s)
一开始的时候在压缩路径和合并的时候就求余,反而把自己绕进去了,其实这两步不需要求余
单纯地求出距离根顺时针偏移量做差%300即可
#include<stdio.h>
const int N=5e4+10;
int fa[N];
int val[N];
int n,m;
//val[i]记录偏离根顺时针多少
int ans=0;
void init(int n)
{
for(int i=1;i<=n;++i)
{
fa[i]=i;
val[i]=0;
}
}
int Find(int x)
{
if(x==fa[x])
return x;
else
{
int tmp=Find(fa[x]);
val[x]=val[x]+val[fa[x]];
fa[x]=tmp;
return tmp;
}
}
void Merge(int x,int y,int s)
{
int fx=Find(x);
int fy=Find(y);
if(fx!=fy)
{
fa[fy]=fx;
val[fy]=val[x]+s-val[y];
//printf("%d\n",val[fy]);
}
else
{
// printf("%d %d\n",val[y],val[x]);
// printf("%d\n",(300+val[y]-val[x])%300);
if((val[y]-val[x])%300!=s)
++ans;
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n);
ans=0;
for(int i=1;i<=m;++i)
{
int x,y,s;
scanf("%d%d%d",&x,&y,&s);
Merge(x,y,s);
}
printf("%d\n",ans);
}
return 0;
}
hdu 3635 Dragon balls
题意:起初,N个城市每个城市有一个龙珠
T操作代表将A号龙珠在的城市的所有龙珠转移到B龙珠在的城市
Q操作代表询问A号龙珠在的城市,所在城市所有龙珠数目,以及A号龙珠的转移次数
分析:A号龙珠所在城市:find(A),所在城市龙之数目num[i]
重点在龙珠转移次数的修改上,龙珠转移次数=自己转移以及爸爸们的转移,在找爸爸的时候把它一层层递归回来eg,所有操作中,爷爷移动1次,爸爸移动1次,在递归的时候,爸爸移动次数=2,自己移动次数=3(找到一个爸爸就意味着移动了,所以自己肯定有一次)
龙珠数目在合并的时候修改
龙珠转移次数在find函数中修改
#include<cstdio>
using namespace std;
const int N=1e4+10;
int fa[N],num[N],cnt[N];
void init(int n)
{
for(int i=1;i<=n;++i)
{
fa[i]=i;
num[i]=1; //城市i有1个龙珠
cnt[i]=0; //龙珠i转移过0次
}
}
int find(int x)
{
if(x==fa[x])
return x;
else
{
int tmp=find(fa[x]);
cnt[x]+=cnt[fa[x]]; //在递归的时候cnt[fa[x]]已经更新好了
return fa[x]=tmp;
}
}
void merge(int x,int y) //城市x的龙珠转移到城市y去
{
int fx=find(x);
int fy=find(y);
if(fx!=fy) //题目中说了两个城市是不同的,所以不会出现fx==fy的情况
{
fa[fx]=fy;
num[fy]+=num[fx];
cnt[fx]++; //注意不是cnt[x]++; 事实上cnt[fx]=1; 因为每次移动都是所在集合的根去移动,而根在合并的时候一定只移动一次,下一次就换根啦
} //这样在搜cnt[x],在find过程中就能把这个加上去了,假设最终根是fy,fy的转移次数在递归里是不会被加进去的
}
int main()
{
int T;
scanf("%d",&T);
int kase=0;
while(T--) //如果a==0,b==0
{
++kase;
int sum=0;
int n,q;
scanf("%d%d",&n,&q);
init(n);
char ch;
int x,y;
for(int i=1;i<=q;++i)
{
scanf("\n%c",&ch);
if(ch=='T')
{
scanf("%d%d",&x,&y);
merge(x,y);
}
else
{
++sum;
scanf("%d",&x);
int ans=find(x);
if(sum==1) printf("Case %d:\n%d %d %d\n",kase,ans,num[ans],cnt[x]);
else printf("%d %d %d\n",ans,num[ans],cnt[x]);
}
}
}
return 0;
}
hdu 3038 区间型问题,感觉很容易想到线段树上去。 一开始觉得可能有点难,但只要结合最上面的博客,想清楚val[i]的含义,就很简单了
1、被多组数据wa怕了,以后看到题直接按照多组数据的格式写好了
2、区间型问题结合前缀和思想,root->i=val[i] 代表sum[i]-sum[root] (其中root始终是小的一端)
按照上面的理解,题目给的是闭区间, 即sum[y]-sum[x-1] 所以更新的时候需要merge(x-1,y)
路径压缩:如果find(x)<find(y) 把y在的合并到x在的 rootx->rooty=rootx->x+x->y+y->rooty=val[x]+s-val[y]
如果find(x)>find(y) 把x在的合并到y在的 rooty->rootx=rooty->y+y->x+x->rootx=val[y]-s-val[x]
判断错误:find(x)==find(y)时 if((val[y]-val[x])!=s) 错误
#include<stdio.h>
const int N=2e5+10;
int fa[N];
int val[N];
int n,m,q;
//root->i=val[i]记录i比根结点的和 令小的为根结点
int ans=0;
void init(int n)
{
for(int i=0;i<=n;++i)
{
fa[i]=i;
val[i]=0;
}
}
int Find(int x)
{
if(x==fa[x])
return x;
else
{
int tmp=Find(fa[x]);
val[x]=(val[x]+val[fa[x]]);
fa[x]=tmp;
return tmp;
}
}
void Merge(int x,int y,int s)
{
int fx=Find(x);
int fy=Find(y);
if(fx!=fy)
{
if(fx<fy)
{
fa[fy]=fx;
val[fy]=val[x]+s-val[y];
}
else
{
fa[fx]=fy;
val[fx]=val[y]-s-val[x];
}
}
else
{
if((val[y]-val[x])!=s)
{
++ans;
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init(n);
ans=0;
for(int i=1;i<=m;++i)
{
int x,y,s;
scanf("%d%d%d",&x,&y,&s);
--x;
Merge(x,y,s);
}
printf("%d\n",ans);
}
return 0;
}