并查集的基本操作:
1.find,查询一个元素属于哪一个集合。
2.merge 把两个集合合并成一个大集合,一般不用写一个额外的函数,直接写主函数内就可以了。
在并查集中,我们采用“待元法”,即为每个集合选择一个固定的元素,作为整个集合的“代表”。使用一个树形结构存储每个集合,书上每个结点都是一个元素,树根是集合的代表元素。


poj 2524


一个基础的例题。
题意:多组输入,处理到文件末尾,每组输入一个n、m,表示有n个同学,m个关系,每个关系输入a、b,表示a、b是相同的宗教,问有多少个不同的宗教。
思路:合并每个a、b的集合,最后遍历1 ~ n,有多少个不同的集合就有多少个不同的宗教。fa[i]的初始值可以是i也可以是-1或者0,赋值为0有时可以偷懒,赋值-1和0是为了可以直接用memset初始化,但是容易出错,出错的一个原因是数据输入中有重边。这里还用到了路径压缩和按秩合并。
原理:
1.路径压缩,每次访问find的操作的同时,把访问过的每个结点直接指向树根,这样下次查找会更快,同时可以避免某条路径特别长。
2.按秩合并,也称“启发式合并”,“秩”定义为树的深度,把“秩”较小的树根作为“秩”较大的树根的子结点,这样就是真正的菊花图了,路径合并还是会出现一条路径比较长的情况(这条路径一直没查询到)。数据1e7的时候用会超内存,1e5的时候最佳,小于1e5几乎没有效果。
3.单独用1或者2,复杂度图片说明 (log n),两个都用复杂度就是反阿克曼函数级别的了,是一个比对数函数增长还慢的函数。
COde

#include<iostream>
using namespace std;
int fa[500005],ran[500005];
inline void init(int n){
    for(int i=1;i<=n;i++){
        fa[i]=i;
        ran[i]=1;
    }
}
int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
inline void merge(int i,int j){
    int x=find(i),y=find(j);
    if(ran[x]<=ran[y])
        fa[x]=y;
    else
        fa[y]=x;
    if(ran[x]==ran[y]&&x!=y)
        ran[y]++;
}
int main(){
    int n,m,a,b,i,j=1;
    while(cin>>n>>m&&(n||m)){
        init(n);
        for(i=1;i<=m;i++){
            cin>>a>>b;
            merge(a,b);
        }
        int ans=0;
        for(i=1;i<=n;i++)    if(fa[i]==i)    ans++;
        cout<<"Case "<<j++<<": "<<ans<<endl;
    }
}

poj 1611


题意
多组输入,每组输入一个n、m,表示n个学生编号0~n-1,分为m组,每组开头一个数字k表示该组有k个同学,问编号为0的同学的那组有多少人,输入以n=0,m=0结束。
思路
直接合并,让0始终为根,因为数据较小,不需要按秩合并,效果不大。
Code

#include<iostream>
using namespace std;
int fa[500005];
inline void init(int n){
    for(int i=0;i<=n-1;i++){
        fa[i]=i;
    }
}
int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
inline void merge(int i,int j){
    int x=find(i),y=find(j);
    if(x==0)    fa[y]=x;
    else    fa[x]=y;
}
int main(){
    int n,m,a,b,k,i;
    while(cin>>n>>m&&(n||m)){
        init(n);
        while(m--){
            cin>>k;
            for(i=1;i<=k;i++){
                if(i!=1){
                    cin>>b;
                    merge(a,b);
                    a=b;
                    continue;
                }
                cin>>a;
            }
        }
        int ans=0;
        for(i=0;i<=n-1;i++)    if(find(i)==0)    ans++;
        cout<<ans<<endl;
    }
}

poj 1703


题意:T组数据,每组数据输入n、m,n,n个罪犯编号1 ~ n,m组操作,D表示A、B不是一个集合的,A表示询问你A、B是一个集合还是不是一个集合又或是还不确定。
思路:比较有趣,并查集擅长动态维护许多具有传递性的关系,等于是连续性的,不等于不是连续性的,因为A、B的关系只有两种,所以A、B不是一个集合可以转化为A和B的对头是一个集合的、B和A的对头是一个集合的,开两倍的数组进行维护,那么就有传递性了。
Code

#include<iostream>
#include<cstdio> 
using namespace std;
#define N 100005
int fa[2*N],ran[2*N];
inline void init(){
    for(int i=1;i<=2*N;i++){
        fa[i]=i;
        ran[i]=1;
    }
}
int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
inline void merge(int i,int j){
    int x=find(i),y=find(j);
    if(x==y)    return;
    if(ran[x]<=ran[y])
        fa[x]=y;
    else
        fa[y]=x;
    if(ran[x]==ran[y])
        ran[y]++;
}
int main(){
    int t,n,m,a,b,i;
    char ch;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        init();
        while(m--){
            scanf(" %c%d%d",&ch,&a,&b);
            if(ch=='D'){
                merge(a,b+N);
                merge(a+N,b);
            }
            else{
                if(find(a)==find(b))
                    printf("In the same gang.\n");
                else if(find(a)==find(b+N))
                    printf("In different gangs.\n");
                else{
                    puts("Not sure yet.");
                }
            }
        }
    }
}

poj 2236


题意:给你N台电脑,从1-N。一个数字,表示两台计算机的最大通信距离,超过这个距离就无法进行通信。然后分别告诉这些电脑的坐标,接下来有两种操作,第一种O表示这点电脑修好,第二种S,表示测试这两台电脑能不能进行正常的通信.
思路:存储修复的电脑,每修复一个电脑就判断能否和前面的电脑合并。
思路

#include<iostream>
#include<cstdio> 
#include<algorithm>
#include<cmath>
using namespace std;
#define N 1001
int fa[N],ran[N],a[1001],b[1001],em[1001];
inline void init(int x){
    for(int i=1;i<=x;i++){
        fa[i]=i;
        ran[i]=1;
    }
}
int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
inline void merge(int i,int j){
    int x=find(i),y=find(j);
    if(x==y)    return;
    if(ran[x]<=ran[y])
        fa[x]=y;
    else
        fa[y]=x;
    if(ran[x]==ran[y])
        ran[y]++;
}
int check(int x,int y,int a,int b,int d){
    int temp=(x-a)*(x-a)+(y-b)*(y-b);
    if(temp>d*d)    return 0;
    return 1;
}
int main(){
    int n,d,i,aa,bb,m,j=0;
    char ch;
    scanf("%d%d",&n,&d);
    init(n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i],&b[i]);
    while(~scanf(" %c",&ch)){
        if(ch=='O'){
            scanf("%d",&m);
            for(i=1;i<=j;i++){
                int temp=em[i];
                if(check(a[temp],b[temp],a[m],b[m],d))
                    merge(temp,m);
            }
            em[++j]=m;
        }
        else{
            scanf("%d%d",&aa,&bb);
            if(find(aa)==find(bb))    printf("SUCCESS\n");
            else
                printf("FAIL\n");
        }
    }
}

poj 2492


题意:给出N条虫子,然后a和b交配,给出M对a和b后问有没有同性恋的虫子(题目意思太BT了)。
思路:和poj 1703一样的思路,只是如果发现a、b是同性(也就是一个集合)的,后面就不用判断,输出有同性恋就可以了。
Code

#include<iostream>
#include<cstdio> 
using namespace std;
#define N 2123
int fa[2*N],ran[2*N];
inline void init(){
    for(int i=0;i<=2*N;i++){
        fa[i]=i;
        ran[i]=1;
    }
}
int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
inline void merge(int i,int j){
    int x=find(i),y=find(j);
    if(x==y)    return;
    if(ran[x]<=ran[y])
        fa[x]=y;
    else
        fa[y]=x;
    if(ran[x]==ran[y])
        ran[y]++;
}

int main(){
    int t,n,m,a,b,i,j=1;
    scanf("%d",&t);
    while(t--){
        int flag=1;
        scanf("%d%d",&n,&m);
        init();
        while(m--){
            if(flag){
                scanf("%d%d",&a,&b);
                int x=find(a),y=find(b);
                if(x==y)
                    flag=0;
                else{
                    merge(a,b+N);
                    merge(a+N,b);
                }
            }
            else
                scanf("%*d%*d");
        }
        if(j>1)
            printf("\n");
        printf("Scenario #%d:\n",j++);
        if(!flag)    printf("Suspicious bugs found!\n");
        else    printf("No suspicious bugs found!\n");
    }
    return 0;
}

poj 1988


题意:有n个元素,开始每个元素自己一栈,有两种操作,将含有元素x的栈放在含有y的栈的顶端,合并为一个栈。第二种操作是询问含有x元素下面有多少个元素。
思路:将含有元素x的栈的父结点设为含有y的栈,用ran[x]记录此时x下面的元素个数,size[y]表示此时含有y的栈的元数个数。
Code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int fa[30005],ran[30005],size[30005];
inline void init(){
    for(int i=1;i<=30005;i++){
        fa[i]=i;
        ran[i]=0;
        size[i]=1;
    }
}
int find(int x){
    if(x==fa[x])    return x;
    else{
        int t=fa[x];
        fa[x]=find(fa[x]);
        ran[x]+=ran[t];
        return fa[x];
    }
}
inline void merge(int i,int j){
    int x=find(i),y=find(j);
    fa[x]=y;
    ran[x]+=size[y];
    size[y]+=size[x];
}
int main(){
    int t,a,b;
    char ch;
    init();
    scanf("%d",&t);
    while(t--){
        scanf(" %c",&ch);
        if(ch=='M'){
            scanf("%d%d",&a,&b);
            merge(a,b);
        }
        else{
            scanf("%d",&a);
            find(a);
            printf("%d\n",ran[a]);
        }
    }
}

poj 1182


题意:根据动物之间的吃与被吃关系通过并查集把他们连起来,输出语句出现矛盾的次数,默认先输入的正确(此矛盾包括:1、自身矛盾,2、与N矛盾)。(并查集的经典题)
思路:出处图片说明 别人的博客。非常详细。
如果两个数在同一个集合(在这里同一个集合表示两者关系已经确定了)里或者自身矛盾已经与N矛盾,直接判断真假,没出现过就把他们合并到一个区间。
合并树时要注意顺序,我这里是把 x 树的根当做主根。找父亲节点时,要不断更新 r[]的值。这里有两个关系:如果 x 和y 为关系 r1, y 和 z 为关系 r2 ,那么 x 和z的关系就是 (r1+r2)%3。

x和fx的关系是ran[x],y和fy的关系是ran[y],y和x的关系是d-1(d=0表示相等,d=1,d=1表示a被b吃,d=2表示a吃b),那么fy 对 fx 的关系是(3-r[y] + d-1 + r[x])%3。
图片说明
Code:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 50000
int fa[N],ran[N],n;
inline void init(){
    for(int i=1;i<=n;i++){
        fa[i]=i;
        ran[i]=0;
    }
}
int find(int x){
    if(x==fa[x])
        return x;
    int t=fa[x];
    fa[x]=find(fa[x]);
    ran[x]=(ran[x]+ran[t])%3;
    return fa[x];
}
inline void merge(int i,int j,int d){
    int x=find(i),y=find(j);
    if(x==y)    return;
    fa[y]=x;
    ran[y]=(3-ran[j]+d-1+ran[i])%3;
}
int main(){
    int m,a,b,d,i,ans=0;
    scanf("%d%d",&n,&m);
    init();
    while(m--){
        scanf("%d%d%d",&d,&a,&b);
        if(a>n||b>n||(d==2&&a==b))    ans++;
        else if(find(a)==find(b)){
            if(d==1&&ran[a]!=ran[b])    ans++;
            if(d==2&&(ran[a]+1)%3!=ran[b])    ans++;
        }
        else
            merge(a,b,d);
    }
    printf("%d\n",ans);
}

hdu 3635


题意:起初球i是被放在i号城市的,在年代更迭,世事变迁的情况下,球被转移了,而且转移的时候,连带该城市的所有球都被移动了:T A B(A球所在的城市的所有球都被移动到了B球所在的城市),Q A(问:A球在那城市?A球所在城市有多少个球呢?A球被转移了多少次呢?)。
思路;一个数组size[x]记录以x为代表的集合的元数个数,ran[x]则记录x被搬运地方次数。
Code

#include<iostream>
#include<cstdio>
using namespace std;
int fa[10005],ran[10005],size[10005];
inline void init(int n){
    for(int i=1;i<=n;i++){
        fa[i]=i;
        ran[i]=0;
        size[i]=1;
    }
}
int find(int x){
    if(x==fa[x])    return x;
    else{
        int t=fa[x];
        fa[x]=find(fa[x]);
        ran[x]+=ran[t];
        return fa[x];
    }
}
inline void merge(int i,int j){
    int x=find(i),y=find(j);
    fa[x]=y;
    size[y]+=size[x];
    ran[x]++;
}
int main(){
    int t,n,q,j=1;
    char ch;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        printf("Case %d:\n",j++);
        init(n);
        while(q--){
            int a,b;
            scanf(" %c",&ch);
            if(ch=='T'){
                scanf("%d%d",&a,&b);
                merge(a,b);
            }
            else{
                scanf("%d",&a);
                int ans=find(a);
                printf("%d %d %d\n",ans,size[ans],ran[a]);
            }
        }
    }
    return 0;
}

hdu 1856


题意:通俗点讲就是有几个人,他们每个人有自己的小团体,要你求他们中人数最多的数量
思路:离散化后可以按秩合并,没有离散化就不能按秩合并,size[x]记录这个集合的元素个数,每次合并时记录最大值就可以了。
Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int fa[10000005],size[10000005];
inline void init(){
    for(int i=1;i<=10000000;i++){
        fa[i]=i;
        size[i]=1;
    }
}
int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
inline int merge(int i,int j){
    int x=find(i),y=find(j);
    if(x!=y){
        fa[x]=y;
        return size[y]+=size[x];
    }
    else
        return size[y];
}
int main(){
    int n,a,b,i;
    while(~scanf("%d",&n)){
        if(n==0){
            printf("1\n");
            continue;
        }
        int ans=0;
        init();
        for(i=1;i<=n;i++){
            scanf("%d%d",&a,&b);
            ans=max(ans,merge(a,b));
        }
        printf("%d\n",ans);
    }
}

hdu 1272


题意:给不知道多少对无向边,构成一条边,问是否联通且无环。(重边也是输出no)
思路:合并时判断有无成环,如果没有成环就判断所有的边是否联通。
Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int fa[100005],ran[100005],vis[100005];
inline void init(){
    for(int i=1;i<=100000;i++){
        fa[i]=i;
        ran[i]=1;
        vis[i]=0;
    }
}
int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
inline int merge(int i,int j){
    int x=find(i),y=find(j);
    if(x==y)    return 0;
    fa[x]=y;
    return 1;
}

int check(){
    int i,x,flag=0;
    for(i=1;i<=100005;i++)
        if(vis[i]){
            if(flag){
                if(find(i)!=x){
                    return 0;
                }
            }
            else{
                x=find(i);
                flag=1;
            }
        }
    return 1;
}
int main(){
    int a=0,b=0,i;
    while(a!=-1){
        int flag=1;
        init();
        while(scanf("%d%d",&a,&b),a&&a!=-1){
            vis[a]=vis[b]=1;
            if(flag)
                flag=merge(a,b);
        }
        if(flag==0)
            printf("No\n");
        else if(a!=-1){
            if(check())
                printf("Yes\n");
            else
                printf("No\n");
        }
    }
}

hdu 1325


题意:和1272一样的输入,但是这里是有向边,第一个整数指向第二个整数。
思路:不能成环,全部联通,任何一个节点的入度不能大于1,重边不要紧。
Code

#include<stdio.h>
#include<string.h>
#define N 11000
int fa[N],vis[N],maxn;
void init(){
    for(int i=1;i<N;i++)
        fa[i]=i;
    memset(vis,0,sizeof(vis));
    maxn=0;
}
int find(int x){
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
int merge(int i,int j,int flag){
    int x=find(i),y=find(j);
    if(x==y||y!=j)    return 0;//入度不为1 
    fa[y]=x;
    return flag;
}
int max(int a,int b){
    return a>b?a:b;
}
int main(){
    int a,b,j=1,flag=1;
    init();
    while(~scanf("%d%d",&a,&b)&&a!=-1){
        if(!a){
            int ans=0;
            for(int i=1;i<=maxn;i++)
                if(vis[i]&&i==fa[i])    ans++;
            if(ans<=1&&flag)
                printf("Case %d is a tree.\n",j++);
            else
                printf("Case %d is not a tree.\n",j++);
            init();
            flag=1;
            continue;
        }
        vis[a]=vis[b]=1;
        maxn=max(maxn,max(a,b));
        flag=merge(a,b,flag);
    }
}

hdu 1198


题意:A ~ K 代表n个管道图,给出一个m * n的二维字符数组,判断当整个图都能流到水时需要放几口井。
思路:判断每个点和它右边、下边是否可以合并,可以就合并,最后判断有几个联通块就可以了。
Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include <vector>
#include<map>
#include<set>
#include<utility>
#define  js  ios::sync_with_stdio(false)
using namespace std;
int fa[2510];
char mapp[55][55];
int find(int x) {
    return fa[x]==-1?x:(fa[x]=find(fa[x]));
}
int main() {
    int n,m;
    while(scanf("%d%d",&n,&m),n+1) {
    memset(fa,-1,sizeof fa);
    for(int i=0;i<n;++i)    scanf("%s",mapp[i]);
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j) {
            if(j!=m-1) {
                if(mapp[i][j]=='B'||mapp[i][j]=='D'||mapp[i][j]=='F'||mapp[i][j]=='G'||mapp[i][j]=='I'||mapp[i][j]=='J'||mapp[i][j]=='K')
                    if(mapp[i][j+1]=='A'||mapp[i][j+1]=='C'||mapp[i][j+1]=='F'||mapp[i][j+1]=='G'||mapp[i][j+1]=='H'||mapp[i][j+1]=='I'||mapp[i][j+1]=='K') {
                        int x=find(i*m+j+1),y=find(i*m+j+2);
                        if(x!=y) fa[y]=x;
                    }
            }
            if(i!=n-1) {
                if(mapp[i][j]=='C'||mapp[i][j]=='D'||mapp[i][j]=='E'||mapp[i][j]=='H'||mapp[i][j]=='I'||mapp[i][j]=='J'||mapp[i][j]=='K')
                    if(mapp[i+1][j]=='A'||mapp[i+1][j]=='B'||mapp[i+1][j]=='E'||mapp[i+1][j]=='G'||mapp[i+1][j]=='H'||mapp[i+1][j]=='J'||mapp[i+1][j]=='K') {
                        int x=find(i*m+j+1),y=find(i*m+j+m+1);
                        if(x!=y) fa[y]=x;
                    }
            }
        }
    int ans=0;
    for(int i=1;i<=n*m;++i)    if(find(i)==i)    ans++;
    printf("%d\n",ans);
    }
}

hdu 2586


题意:一个村子里有n个房子,这n个房子用n-1条路连接起来,接下了有m次询问,每次询问两个房子a,b之间的距离是多少。
思路:最近公共祖先或者并查集+深搜。因为数据弱,我并查集的思路很简单,不去路径合并(其实路径合并也可以),size[x]=d,表示x到父结点的距离是d,询问的时候算出每个结点到根的距离,然后相减就可以了。
Code
LCA代码戳我

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include <vector>
#include<map>
#include<set>
#include<utility>
#define  js  ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
int fa[40005],size[40005],ans1=0,ans2=0;
inline init(int x){
    for(int i=1;i<=x;++i) {
        fa[i]=i;
        size[i]=0;
    }
}
void merge(int i,int j,int d) {
    if(size[i]) {
        fa[j]=i;
        size[j]=d;
    }
    else {
        fa[i]=j;
        size[i]=d;
    }
}
int find1(int x){
    if(fa[x]==x)    return ans1;
    ans1+=size[x];
    find1(fa[x]);
}
int find2(int x){
    if(fa[x]==x)    return ans2;
    ans2+=size[x];
    find2(fa[x]);
}
int main() {
    int t,n,m;
    js;
    cin>>t;
    while(t--) {
        int a,b,c;
        cin>>n>>m;
        init(n);
        for(int i=1;i<=n-1;++i) {
            cin>>a>>b>>c;
            merge(a,b,c);
        }
        for(int i=1;i<=m;++i) {
            cin>>a>>b;
            ans1=ans2=0;
            cout<<abs(find1(a)-find2(b))<<endl;
        }
    }
} 

hdu 6109


题意:m条信息由ans组数据组成,每条信息的最后一个整数e=1表示A、B相等,e=0表示A、B不相等,每组数据加上最后一条信息就会矛盾,不加上就不会矛盾,问ans的值,并输出每组输出有几条信息。
思路:用set存出现过的数字,便于找下一组数据之前初始化,再用一个set存结点i的对头,每次合并时将被合并的数的对头移到父亲的对头集合里,再把被合并的数从对头的对头集合里抹掉。如果x、y是相等的,但是不是一个集合或y是x的对头,或x、y不相等,但是x、y是一个集合的,那么这是一组数据的结尾。(理论上是可以开两倍的fa[]数组去写不用set,就和1703的思路差不多,但是这是很久以前写的,我也就没试,江大佬亲自验证是错的)
Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include <vector>
#include<map>
#include<set>
#include<utility>
#define  js  ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
int fa[100005],ran[100005],ans[100005];
set<int>st;
set<int>s[100005];
inline void init(){
    set<int>::iterator it;
    for(it=st.begin();it!=st.end();++it) {
        fa[*it]=-1;
        ran[*it]=1;
        s[*it].clear();
    }
    st.clear();
}
int find(int x) {
    return fa[x]==-1?x:(fa[x]=find(fa[x]));
}
void merge(int i,int j) {
    if(i==j)    return;
    set<int>::iterator it;
    if(ran[i]==0)    ran[i]=1;
    if(ran[j]==0)    ran[j]=1;
    if(ran[i]>ran[j])    {
        for(it=s[j].begin();it!=s[j].end();it++) {
            s[*it].erase(j);
            s[*it].insert(i);
            s[i].insert(*it);
        }
        fa[j]=i;
    }
    else{
        for(it=s[i].begin();it!=s[i].end();it++) {
            s[*it].erase(i);
            s[*it].insert(j);
            s[j].insert(*it);
        }
        fa[i]=j;
    }
    if(ran[i]==ran[j])    ran[j]++;
}
int main() {
    int T,a,b,e,x,y,sum=0,jy=0;
    js;
    cin>>T;
    memset(fa,-1,sizeof fa);
    while(T--) {
        cin>>a>>b>>e;
        st.insert(a),st.insert(b);
        x=find(a),y=find(b);
        sum++;
        if(e==1) {
            if(x==y||s[x].count(y)==0) {
                merge(x,y);
            }
            else {
                ans[++jy]=sum;
                sum=0;
                init();
            }
        }
        else {
            if(x==y) {
                ans[++jy]=sum;
                sum=0;
                init();
            }
            else {
                s[x].insert(y);
                s[y].insert(x);
            }
        }
    }
    cout<<jy<<endl;
    for(int i=1;i<=jy;++i) cout<<ans[i]<<endl;
    return 0;
}