/*
在维护并查集的同时维护一个sex数组,记录任一小虫的异性的编号。
例如输入a,b
如果小虫a的异性为空,那么sex[a]=b;
若不为空,那么就将b和小虫的异性并起来,union(b,sex[a])
因为b虫必然和小虫a的异性在一个集合里。
在每次输入两个虫的编号的时候,只用查是否在同一集合即可。
*/
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=100005;
int pa[MAXN],sex[MAXN];
int find_set(int a){
if(a!=pa[a]){;
return pa[a]=find_set(pa[a]);
}
return a;
}
int union_set(int a,int b){
a=find_set(a);
b=find_set(b);
if(a==b) return 0;
else{
pa[b]=a;
return 1;
}
}
int main(){
int T,n,m,k,a,b,apple=1;
scanf("%d",&T);
while(T--){
k=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) pa[i]=i;
memset(sex,0,sizeof(sex));
for(int i=0;i<m;i++){
scanf("%d%d",&a,&b);
if(k){
a=find_set(a);
b=find_set(b);
if(a==b) k=0;
if(sex[a]==0) sex[a]=b;
else union_set(sex[a],b);
if(sex[b]==0) sex[b]=a;
else union_set(sex[b],a);
}
}
printf("Scenario #%d:\n",apple++);
if(k) printf("No suspicious bugs found!\n\n");
else printf("Suspicious bugs found!\n\n");
}
return 0;
}
Is It A Tree?
/*
这道题跟小希的迷宫有很大的相似吧,只是一个是无向图一个是有向图。
也是给你那些结点之间的信息,然后让你判断是不是一颗树罢了,用树的定义来 判断吧,
无环,n个结点最多有n-1条边,不然就会有环。只有一个入度为0的结点,不存在入度大于1的结点。
这些也足以判断是否为一棵树了吧。不过要注意 一些特殊数据的情况,空树也是树。比如输入0 0。
解决方法:
其实也可以不用并查集,这样就可以直接按照上面的条件来统计,就可以判断是不是一颗树了。
自我感言:额,这道题目关键就是判断树的那几个标准,1,无环;2,除了根,所有的入度为1,
根入度为0;3,这个结构只有一个根,不然是森林了。
这是三个标准拿捏好,再注意这里空树也是树。基本上就ok了。。
坑爹的是下边的代码可以在hdu上通过,而不能在poj上通过。。。。。
看了discuss才知道 1 1 0 0 不能是树。
下面几种情况要注意了。。。。
1: 0 0 空树是一棵树
2: 1 1 0 0 不是树 不能自己指向自己
3: 1 2 1 2 0 0 不是树....自己开始一直在这么WA 好郁闷 重复都不行呀~~5555
4: 1 2 2 3 4 5 不是树 森林不算是树(主要是注意自己)
5: 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 1 注意 一个节点在指向自己的父亲或祖先 都是错误的
即 9-->1 错
6: 1 2 2 1 0 0 也是错误的
这样基本上就可以在poj上通过了,poj(1038)题
*/
#include <stdio.h>
const int max_num = 100000+10;
typedef struct
{
int num,root,conn;//数据、根、入度
}Node;
Node node[max_num];
void init()
{
for(int i = 0; i < max_num; i++)
{
node[i].conn = 0;//入度初始化为0
node[i].root= i;//根记录为自身
node[i].num=0;//标记数字是否被使用过,0:没有被使用过,1:使用过了
}
}
int find_root(int a)
{
if(node[a].root!=a)
return node[a].root = find_root(node[a].root);
return node[a].root;
}
void union_set(int a,int b)
{
a = find_root(a);
b = find_root(b);
if(a==b)//同一个根,说明是在同一个树下
return;
node[b].root=a;//把b的根赋为a的根,此时a已经是根,num==root
}
int main()
{
int n,m;
int i = 1;
bool flag=true;//true:是个树,false:不是树
init();
while(scanf("%d%d",&n,&m)!=EOF&&n>=0&&m>=0)
{
if(!flag&&n!=0&&n!=0)continue;//已经确定不是树了,就继续循环
if(n==0&&m==0)
{
int root_num=0;
for(int j = 1; j < max_num;j++)
{
//判断是否为森林,如果,root_num用来记录根的数目
if(node[j].num && find_root(j)==j)
root_num++;
if(node[j].conn>1)//如果出现某个节点的入度超过1,不是树
{
flag = false;
break;
}
}
if(root_num>1)//连通分支大于1,是森林不是树
flag=false;
if(flag)
printf("Case %d is a tree.\n",i++);
else printf("Case %d is not a tree.\n",i++);
flag = true;
init();
continue;
}
if(m!=n&&find_root(n)==find_root(m))
flag = false;
else
{
//将m,n,记录为节点
node[m].num = 1;
node[n].num = 1;
node[m].conn++;//入度增加一
union_set(n,m);
}
}
return 0;
}
Segment set
/*
判断线段相交, 如果有两条线段不相交,但又另外一条线段与这两条线段相交,
则这三条线段两两相交。
*/
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
const double eps = 1e-12;
const int size = 1000;
struct point
{
double x, y;
};
struct seg
{
point a, b;
} S[size + 20];
double multi ( point p1, point p2,point p0)
{
return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}
bool isIntersected(point s1, point e1, point s2, point e2)
{
if(
(max(s1.x, e1.x) >= min(s2.x, e2.x)) &&
(max(s2.x, e2.x) >= min(s1.x, e1.x)) &&
(max(s1.y, e1.y) >= min(s2.y, e2.y)) &&
(max(s2.y, e2.y) >= min(s1.y, e1.y)) &&
(multi(s2, e1, s1) * multi(e1, e2, s1) >= -eps) &&
(multi(s1, e2, s2) * multi(e2, e1, s2) >= -eps)
) return true;
return false;
}
int father[size + 10], n, rank[size + 10];
int Find_Set(int x)
{
if (x != father[x])
{
father[x] = Find_Set(father[x]);
}
return father[x];
}
void Union(int x, int y)
{
x = Find_Set(x);
y = Find_Set(y);
if (x != y)
{
father[x] = y;
}
}
int main()
{
int t;
char ST[2];
scanf("%d", &t);
int tt = t;
while (t --)
{
if (t != tt - 1)printf("\n");
scanf("%d", &n);
int j = 0;
for (int i = 0; i < n; i ++)
{
scanf("%s", ST);
if (ST[0] == 'P')
{
scanf("%lf%lf%lf%lf", &S[j].a.x, &S[j].a.y, &S[j].b.x, &S[j].b.y);
father[j] = j;
for (int k = 0; k < j; k ++)
{
if (isIntersected(S[j].a, S[j].b, S[k].a, S[k].b))
{
Union (j, k);
}
}
j ++;
}
else if (ST[0] == 'Q')
{
int s, sum = 0;
scanf("%d", &s);
for (int k = 0; k < j; k ++)
if (Find_Set(s - 1) == Find_Set(k))sum ++;
printf("%d\n", sum);
}
}
}
return 0;
}
find the most comfortable road
/*
主要运用了并查集,和贪心,先把所有公路的速度,由小到大排序,然后一条一条的取,
最后所有公路差的最大值就是结果。
*/
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 205;
const int maxm = 1005;
const int inf = (0x7f7f7f7f);
#define min(a,b) ((a)<(b)?(a):(b))
int n, m, s, t;
int ans;
int fa[maxn];
struct Edge
{
int s, e, speed;
}edge[maxm];
int cmp(Edge a, Edge b)
{
return a.speed < b.speed;
}
int find(int x)
{
while (fa[x] != x) x = fa[x];
return x;
}
int main()
{
while (scanf("%d %d", &n, &m) != EOF)
{
int i, j;
for (i=0; i<m; i++)
{
scanf("%d %d %d", &edge[i].s, &edge[i].e, &edge[i].speed);
}
sort(edge, edge+m, cmp);
int q;
scanf("%d", &q);
while (q--)
{
scanf("%d %d", &s, &t);
int min = inf;
for (i=0; i<m; i++) //枚举
{
for (j=1; j<=n; j++) fa[j] = j;
for (j=i; j<m; j++)
{
int fx = find(edge[j].s);
int fy = find(edge[j].e);
if (fx != fy) fa[fx] = fy;
if (find(s) == find(t))
{
min = min(min, edge[j].speed - edge[i].speed);
break;
}
}
}
if (min == inf) puts("-1");
else printf("%d\n", min);
}
}
return 0;
}
Code Lock
/*
用并查集和二分求26^x
*/
#include <iostream>
using namespace std;
#define MAX 10000010
int root[MAX];
#define MOD 1000000007
int find(int x)
{
if(root[x]!=x)
{
root[x]=find(root[x]);
}
return root[x];
}
long long binary(int x)
{
if(x==0)return 1;
long long a=binary(x/2);//a need to be long long
a=a*a%MOD;
if(x%2==1)a=a*26%MOD;
return a;
}
bool merge(int l,int r)
{
int fl=find(l);
int fr=find(r);
if(fl==fr)
return false;
else
{
root[fr]=fl;
return true;
}
}
int main()
{
int n,m;
long long ans;
while(cin>>n>>m)
{
int i;
for(i=0;i<=n;i++)
{
root[i]=i;
}
int x=0;
while(m--)
{
int l,r;
cin>>l>>r;
l--;
if(merge(l,r))
x++;
}
ans=binary(n-x);
cout<<ans<<endl;
}
system("pause");
return 0;
}
Dragon Balls
/*
想到了并查集就是暴虐了。
主要是记录移动次数,其实每个根结点都是最多移动一次的,
所以记录移动次数把自己的加上父亲结点的就是移动总数了
*/
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=10010;
int F[MAXN];
int num[MAXN];
int move[MAXN];
int n;
void init()
{
for(int i=1;i<=n;i++)
{
F[i]=-1;
num[i]=1;
move[i]=0;
}
}
int find(int x)
{
if(F[x]==-1)return x;
int t=F[x];
F[x]=find(F[x]);
move[x]+=move[t];
return F[x];
}
void bing(int a,int b)
{
int t1=find(a);
int t2=find(b);
if(t1!=t2)
{
F[t1]=t2;
num[t2]+=num[t1];
move[t1]=1;
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int m;
int T;
char str[10];
int a,b;
int iCase=0;
scanf("%d",&T);
while(T--)
{
iCase++;
scanf("%d%d",&n,&m);
init();
printf("Case %d:\n",iCase);
while(m--)
{
scanf("%s",&str);
if(str[0]=='T')
{
scanf("%d%d",&a,&b);
bing(a,b);
}
else
{
scanf("%d",&a);
int t=find(a);
printf("%d %d %d\n",t,num[t],move[a]);
}
}
}
return 0;
}
Virtual Friends
/*
题意:给你两个人,他们刚成为朋友,问在这两个人的社交圈里有多少个朋友?
提示:朋友的朋友也是你的朋友。
分析:用字典树解决,注意下面这组数据:
1
6
Fred Barney
Barney Betty
Betty Wilma
AAAAA BBBBB
AAAA BBBBB
AAAA Fred
输出结果应是:
2
3
4
2
3
7
*/
#include<iostream>
#include<string>
#include<map>
using namespace std;
#define MAX 100005
int father[MAX],sum[MAX];
int total;
map<string,int>A;//用map来处理人名与数字下标之间的对应关系
int find_set(int x)
{
if(x!=father[x])
father[x]=find_set(father[x]);
return father[x];
}
void union_set(int x,int y)
{
x=find_set(x);
y=find_set(y);
if(x!=y)
{
father[x]=y;
sum[y]+=sum[x];
}
}
int main()
{
int t,n;
string a,b;
while(scanf("%d",&t)!=EOF)
{
while(t--)
{
total=0;
A.clear();
scanf("%d",&n);
while(n--)
{
cin>>a>>b;
if(A.find(a)==A.end())
{
total++;
A[a]=total;
father[total]=total;
sum[total]=1;
}
if(A.find(b)==A.end())
{
total++;
A[b]=total;
father[total]=total;
sum[total]=1;
}
union_set(A[a],A[b]);
int ans=find_set(A[b]);
printf("%d\n",sum[ans]);
}
}
}
return 0;
}
How Many Answers Are Wrong
/*
这题大意是给出多个区间的和,判断数据矛盾的区间有几个,比方说【1,5】 = 10 ,
【6.10】 = 10, 【1, 10】 = 30,这明显第三个与前面两个矛盾。
并查集的题,用sum[i]记录1到i的和,则输入的时候左端点a, 跟右端点b,
就可以看作是两个独立的数据
sum[a], sum[b]表示 1到a, 1到b的和。如果a, b有相同的跟i, 即存在[i, a]与[i, b],
只需判断[b, i] - [a, i] 是否等于 c 即可。
*/
#include<stdio.h>
int n, m, data, ans;
int root[200001];
int sum[200001];
int find( int x)
{
int t;
if( x == root[x])
return root[x];
t = root[x];
root[x] = find( root[x]);
sum[x] += sum[t]; //回溯的过程中计算sum[x]到根的大小
return root[x];
}
int merge(int x, int y)
{
int a, b;
a = find(x);
b = find(y);
if( a == b)
{
if( sum[y] != sum[x] + data)
ans++;
return 0;
}
if( a > b)
{
sum[a] = sum[y] - sum[x] - data;
root[a] = b;
}
else
{
sum[b] = sum[x] + data - sum[y];
root[b] = a;
}
return 0;
}
void init()
{
int i;
for( i = 0 ; i <= n ; i++)
{
root[i] = i;
sum[i] = 0;
}
}
int main()
{
int i, j, star, end;
while(scanf("%d%d", &n, &m) != EOF)
{
ans = 0;
init();
for(i = 0 ; i < m ; i++)
{
scanf("%d%d%d", &star, &end, &data);
merge(star - 1, end);
}
printf("%d\n", ans);
}
return 0;
}