申請SAE

如果您發現本博客的外觀很難看,那是因為部分外觀文件被中國.國家.防火.牆屏.蔽所致!
請翻~牆!

我的Wordpress博客的地址: http://zhuyf.tk/
顯示具有 並查集 標籤的文章。 顯示所有文章
顯示具有 並查集 標籤的文章。 顯示所有文章

2012年2月28日 星期二

並查集練習題 島國 jx 題解

【題目描述】
很久很久很久很久很久很久以前......
有一個島國。
這個國家的領地是一塊座標從(1,1)到(K,K)的正方形(包括領海和領陸,座標(x,y)是指(x,y)這塊土地,並非一個點)
衛星資訊會告訴你這個國家的土地情況,希望你能根據給出的資訊計算出這個國家有多少個島。
衛星給出的資訊形如x1 y1 x2 y2,表示左下角座標為x1,y1,右上角座標為x2,y2的這一個矩形區域是陸地
輸入格式:
第一行一個整數n,表示衛星會傳送給你n條資訊
下面n行每行有4個整數,x1,y1,x2,y2,含義如上
輸出格式:
第一行,一個整數Sum,表示這個國家的島的數量
注,只有一個公共點的兩塊陸地不算是一塊區域,具體如樣例

2012年2月1日 星期三

Tyvj1721(寒假模擬賽提高組) 島嶼 解題報告

  島嶼 

描述 Description
湖面上有n座島嶼,從1~n編號。現在要湖上建橋使得島嶼連接起來。橋雙向通行。

輸入格式 Input Format
輸入第一行有兩個整數n,m。
接下來是m行,按照時間順序每行是一次詢問。每行第一個整數q代表詢問的內容:如果q=1,則接下來是兩個島嶼編號a,b(a≠b);如果q=2,則接下來是一個島嶼編號c。 

輸出格式 Output Format
對於每個詢問,按次序各輸出一行作爲回答:
q=1時:如果a,b相互可達,則輸出Yes;如果a,b相互不可達,則輸出No,並在a,b之間建一座橋。
q=2時:輸出一個整數x,表示由c出發可以到達的島嶼有x個(不包括c自身)。

2012年1月2日 星期一

[並查集]NOI2001 食物鏈 eat 解題報告

Title: 食物链    題目連結:COGS
Input: eat.in
Output: eat.out
Time Limit: 1000 ms
Memory Limit: 128 MB
Level: ★★☆

动物王国中有三类动物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),输出假话的总数。

输入文件
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
  • 若D=1,则表示X和Y是同类。
  • 若D=2,则表示X吃Y。
输出文件
只有一个整数,表示假话的数目。
输入样例
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
输入文件
输出样例
3
样例说明
对7句话的分析
  • 1 101 1 假话
  • 2 1 2 真话
  • 2 2 3 真话
  • 2 3 3 假话
  • 1 1 3 假话
  • 2 3 1 真话
  • 1 5 5 真话

「分析」

本題是並查集的經典應用。
每次判斷時,前兩個條件都能O(1)判斷,關鍵在於第3個條件。
由於無法判斷可以另外虛設2*N個物種,分別表示能吃第i個物種的物種和被第i個物種所吃的物種。那麼剩下就是裸並查集了,每次合併就把那個三角環依次合併即可。

「我的代碼」

 

C++语言: Codee#24995
001 /*
002 *Problem: NOI2001-eat
003 *Author: Yee-fan Zhu
004 *Time: Jan 02,2012
005 *Method: UFS
006 */
007 #include <iostream>
008 #include <cstdlib>
009 #include <cstdio>
010 using namespace std;
011 const int MAX=150010;
012
013 int N,K;
014 int Ani[MAX];
015 int Eat[MAX];
016 int Eaten[MAX];
017 int False=0;
018
019 int UFS_Find(int x)
020 {
021     int t,p;
022     p=x;
023     while(Ani[p]!=p)
024         p=Ani[p];
025     while(Ani[x]!=x//Compress Path
026     {
027         t=Ani[x];
028         Ani[x]=p;
029         x=t;
030     }
031     return p;
032 }
033
034 bool UFS_Check(int a,int b)
035 {
036     int x=UFS_Find(a);
037     int y=UFS_Find(b);
038     return (x==y);
039 }
040
041 void UFS_Merge(int a,int b)
042 {
043     Ani[UFS_Find(b)]=UFS_Find(a);
044 }
045
046 void init()
047 {
048     scanf("%d %d\n",&N,&K);
049     for (int i=1;i<=N;i++)
050     {
051         int x=N+i;
052         int y=N*2+i;
053         Ani[i]=i,Ani[x]=x,Ani[y]=y;
054         Eaten[i]=x,Eat[i]=y;
055         Eaten[x]=y,Eat[x]=i;
056         Eaten[y]=i,Eat[y]=x;
057     }
058 }
059
060 void work()
061 {
062     int D,X,Y;
063     for (int i=1;i<=K;i++)
064     {
065         scanf("%d %d %d\n",&D,&X,&Y);
066         if(X>N || Y>N)
067         {
068             False++;
069             continue;
070         }
071         if(D==2 && X==Y)
072         {
073             False++;
074             continue;
075         }
076
077         if(D==1)
078         {
079             if(UFS_Check(X,Y))
080                 continue;
081             if(UFS_Check(Eat[X],Y) ||  UFS_Check(Eaten[X],Y) )
082             {
083                 False++;
084                 continue;
085             }
086
087             UFS_Merge(X,Y);
088             UFS_Merge(Eaten[X],Eaten[Y]);
089             UFS_Merge(Eat[X],Eat[Y]);
090             continue;
091         }
092
093         if(D==2)
094         {
095             if(UFS_Check(Eat[X],Y))
096                 continue;
097
098             if(UFS_Check(X,Y) || UFS_Check(Eaten[X],Y))
099             {
100                 False++;
101                 continue;
102             }
103
104             UFS_Merge(Eat[X],Y);
105             UFS_Merge(X,Eaten[Y]);
106             UFS_Merge(Eaten[X],Eat[Y]);
107             continue;
108         }
109     }
110 }
111
112 int main()
113 {
114     freopen("eat.in","r",stdin);
115     freopen("eat.out","w",stdout);
116     init();
117     work();
118     printf("%d\n",False);
119     return 0;
120 }

2011年12月26日 星期一

[並查集]HAOI 破譯密文 encrypt 解題報告

【題目描述】  查看題目
原文鏈接:http://yeefan.tk/blog/haoi-encrypt/
資訊的明文是由0利1組成的非空序列。但在網絡通信中,為了資訊的安全性,常對明文進行加密,用密文進行傳輸。密文是由0、1和若干個密碼字母組成,每個密碼字母代表不同的01串,例如,密文二011a0bf00a01。密碼破譯的關鍵是確定每個密碼的含義。
經過長期統計分析,現在知道了每個密碼的固定長度,如今,我方又截獲了敵方的兩段密文S1和S2,並且知道S1二S2,即兩段密文代表相同的明文。你的任務是幫助情報人員對給定的兩段密文進行分析,看一看有多少種可能的明文。
【輸入文件】
第1行: S1 (第1段密文)
第2行: S2 (第2段密文)
第3行: N (密碼總數, N<=26)
第4—N+3行: 字母i 長度i (密碼用小寫英文字母表示, 密碼長度<=100)

【輸出文件】
M(表示有M種可能的明文)

【輸入輸出樣例】
encrypt.in
100ad1
cc1
4
a 2
d 3
c 4
b 50
encrypt.out
2
【約束條件】
明文的長度<=10000

【分析】
既然每個密文代表一段明文,那我們可以把密文展開
如樣例的:
100ad1
cc1
4
a 2
d 3
c 4
b 50
我們就可以展開成
1 0 0 a1 a2 d1 d2 d3 1
c1 c2 c3 c4 c1 c2 c3 c4 1
那麼問題就成了在這麼一串數中找出有多少個數不定集合.
即c1代表1 c2代表0 c3代表0
但是 c4和a1共同代表什麼呢,這是問題的關鍵,這就用到了並查集:
首先 1 0 a1 a2…d2 d3都是獨立的集合
從i=1開始向右掃描
先把1所在的集合和c1所在的集合歸成一類,
0所在的集合和c2所在的集合歸成一類
….
最後一定是
代表0的字母有一個集合
代表1的字母有一個集合
不能確定代表什麼的有x個集合
因為每個集合代表的數字要麼是1要麼是0有兩種情況
所以兩個集合有4種情況
三個集合有8種情況
x個集合有2^x種情況
所以我們只需要用並查集算出有幾個非0或1的集合,再累乘後輸出即可
注意:有一個密文既對應0也對應1,這種情況構不成任何正確的明文,所以應當輸出0。

【我的代碼】
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;
int num[30];
 
const char base='a'-1;
const int MAXN=3000;
int Letter[30];//每個密碼的密碼長度
int Num[30][102];
char S1[105];
char S2[105];
int N;//密碼總數
int T=0;//不定集合的數量
int Sum=0;//最終結果
int M=0;
int DealA[MAXN]={0};
int DealB[MAXN]={0};
int Num0;
int Num1;
int Len=0;
 
class Node
{
public:
 int parent;
 int count;
}UFS[MAXN];
 
void UFS_Init(int i)
{
 UFS[i].count=1;
 UFS[i].parent=i;
}
 
int UFS_Find(int x)
{
 int i=x;
 while(i!=UFS[i].parent)
  i=UFS[i].parent;
 
 int j=x;
 while(j!=i)
 {
  int tmp=UFS[j].parent;
  UFS[j].parent=i;
  j=tmp;
 }
 return i;
}
 
void UFS_Merge(int x,int y)
{
 x=UFS_Find(x);
 y=UFS_Find(y);
 if(x==y)
  return;
 
 if(UFS[x].count>UFS[y].count)
 {
  UFS[y].parent=x;
  UFS[x].count+=UFS[y].count;
 }
 else
 {
  UFS[x].parent=y;
  UFS[y].count+=UFS[x].count;
 }
}
 
void init()
{
 scanf("%s\n%s\n",&S1,&S2);
 scanf("%d\n",&N);
 memset(Letter,0,sizeof(Letter));
 char cTmp;
 int iTmp;
 for (int i=1;i<=N;i++)
 {
  scanf("%c %d\n",&cTmp,&iTmp);
  Letter[cTmp-base]=iTmp;
 } 
 
 for(int i=1;i<=26;i++)
 {
  for (int j=1;j<=Letter[i];j++)
  {
   M++;
   Num[i][j]=M;
   UFS_Init(M);
  }
 }
 
 M++;
 Num0=M;
 UFS_Init(M);
 
 M++;
 Num1=M;
 UFS_Init(M);
 
 int top=0;
 for (unsigned int i=0;i<strlen(S1);i++)
 {
  if(isalpha(S1[i]))
  {
   for (int j=1;j<=Letter[ S1[i]-base ]; j++)
   {
    top++;
    DealA[top]=Num[S1[i]-base][j];
   }
  }
  else
  {
   if(S1[i]=='0')
   {
    top++;
    DealA[top]=Num0;
   }
   else
   {
    top++;
    DealA[top]=Num1;
   }
  }
 }
 
 Len=top;
 top=0;
 for (unsigned int i=0;i<strlen(S2);i++)
 {
  if(isalpha(S2[i]))
  {
   for (int j=1;j<=Letter[ S2[i]-base ]; j++)
   {
    top++;
    DealB[top]=Num[S2[i]-base][j];
   }
  }
  else
  {
   if(S2[i]=='0')
   {
    top++;
    DealB[top]=Num0;
   }
   else
   {
    top++;
    DealB[top]=Num1;
   }
  }
 } 
}
 
int work()
{
 for(int i=1;i<=Len;i++)
 {
  UFS_Merge(DealA[i],DealB[i]);
 }
 
 if(UFS_Find(Num0)==UFS_Find(Num1))
 {
  return 0;
 }
 
 bool Used[MAXN];
 memset(Used,0,sizeof(Used));
 
 int Find_Num0=UFS_Find(Num0);
 int Find_Num1=UFS_Find(Num1);
 for(int i=1;i<=Len;i++)
 { 
  int tmp=UFS_Find(DealA[i]);
  if(tmp==Find_Num0 || tmp==Find_Num1 || Used[tmp])
   continue;
 
  T++;
  Used[tmp]=true;
 }
 
 for(int i=1;i<=Len;i++)
 {
  int tmp=UFS_Find(DealB[i]);
  if(tmp==Find_Num0 || tmp==Find_Num1 || Used[tmp])
   continue;
 
  T++;
  Used[tmp]=true;
 }
 
 Sum=1;
 for (int i=1;i<=T;i++)
  Sum*=2;
 return Sum;
}
 
int main()
{
 freopen("encrypt.in","r",stdin);
 freopen("encrypt.out","w",stdout);
 init();
 printf("%d\n",work());
 return 0;
}

2011年11月23日 星期三

[最小生成樹]USACO Dec07 Silver: Building Roads 建造路徑 roads 解題報告

【題目描述】
譯 by CmYkRgB123

Farmer John 剛剛得到了幾個新農場!他想把這幾個農場用路連接起來,這樣他就可以通過筆直的公路從一個農場到另一個農場了。現在已經有了幾條連接着的農場。
N (1 ≤ N ≤ 1,000) 個農場中,每個農場的位置在座標平面的 (Xi, Yi) (0 ≤ Xi ≤ 1,000,000; 0 ≤ Yi ≤ 1,000,000)。已經有 M (1 ≤ M ≤ 1,000) 條路以前就被建好了。請你幫助 Farmer John 考慮建設儘量少長度的額外的路,使他的農場連在一起。
輸入
* 第 1 行: 兩個整數: N , M
* 第 2..N+1 行: 兩個整數 Xi , Yi
* 第 N+2..N+M+2 行: 兩個整數: i , j, 表示已經存在從農場i到農場j的路。
輸出
* 第 1 行: 額外的路的最少長度,保留2小數。 請使用 64 位的浮點數。
樣例輸入
4 1
1 1
3 1
2 3
4 3
1 4
樣例輸出
4.00

【分析】
圖論,最小生成樹問題。這是個包含M條邊的最小生成樹問題。
可以參見BYVoid大犇的解題報告:

以下題解來自BYVoid:
求包含給定的M條邊的最小生成樹。
可以用Kruskal算法加並查集,再讀入M條邊的時候先把這些邊加入樹中,再把最小的不構成環的N(N-1)/2-1-M條邊加入樹,算出邊權和。

【我的代碼】
用了樹狀並查集+路徑壓縮+Kruskal算法,不過還是很慢,10組數據總耗時將近3秒! 我在我的代碼下面給出了BYVoid大犇的代碼,速度巨快!

我的代碼:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;
int N,M;
double ans=0;
bool used[1001][1001];
int B=0;
int X[1001];
int Y[1001];

class Road
{
public:
    int s,e;
    double len;
}R[1000001];

struct Node
{
    int parent; //父节点编号
    int count; //集合中元素的个数
}UnionFindSet[1000001];   
   
int top=0;
   
void InitUFS(int n)
{
    for(int i=1; i<=N;++i)
    {
        UnionFindSet[i].count = 1;
        UnionFindSet[i].parent = i;
    }
}

int Find(int x)
{
    int i = x;
    while(i != UnionFindSet[i].parent)
        i = UnionFindSet[i].parent;

    int j = x;
    while(j != i)
    {
        int tmp = UnionFindSet[j].parent;
        UnionFindSet[j].parent = i;
        j = tmp;
    }
    return i;
}

void Union(int x, int y)
{
      x = Find(x);
      y = Find(y);
      if(y == x) return;
      if(UnionFindSet[x].count > UnionFindSet[y].count)
      {
            UnionFindSet[y].parent = x;
            UnionFindSet[x].count += UnionFindSet[y].count;
      }
      else
      {
            UnionFindSet[x].parent = y;
            UnionFindSet[y].count += UnionFindSet[x].count;
      }
}

double GetDist(int x,int y)
{
    double tmp1,tmp2;
    tmp1=X[x]-X[y];
    tmp2=Y[x]-Y[y];
    double tmp=tmp1*tmp1+tmp2*tmp2;
    return sqrt(tmp);
}

int cmp(const void *a,const void *b)
{
    class Road *c=(class Road *)a;
    class Road *d=(class Road *)b;
    if(c->len<d->len)
        return -1;
    return 1;
}

void Kruskal()
{
    B=0;
    int NC=N*(N-1)/2;
    int t=1;
    int ts,te;
    while(B<NC-1-M &&t<=NC)
    {
        ts=R[t].s;
        te=R[t].e;
        if(Find(ts)!=Find(te))
        {
            ans+=R[t].len;
            Union(ts,te);
            B++;
        }
        t++;
    }
    printf("%.2lf\n",ans);
}

void init()
{
    scanf("%d %d\n",&N,&M);
    for(int i=1;i<=N;i++)
    {
        scanf("%d %d\n",&X[i],&Y[i]);
        InitUFS(i);
    }
    int a,b;
    for (int i=1;i<=M;i++)
    {
        scanf("%d %d\n",&a,&b);
        used[a][b]=true;
        used[b][a]=true;
        if(Find(a)!=Find(b))
        {
            Union(a,b);
            B++;
        }
    }
   
    if(B==N-1)
    {
        printf("%.2lf\n",ans);
        return;
    }
   
    double x;
    for (int i=1;i<=N;i++)
        for (int j=1;j<=N;j++)
            if(!used[i][j])
            {
                x=GetDist(i,j);
                if(x!=0)
                {
                    R[++top].s=i;
                    R[top].e=j;
                    R[top].len=x;
                }
            }
    qsort(R+1,top,sizeof(R[0]),cmp);
    Kruskal();
}

int main()
{
    freopen("roads.in","r",stdin);
    freopen("roads.out","w",stdout);
    init();
    fclose(stdin);
    fclose(stdout);
    return 0;
}


BYVoid大犇的代碼:
//最小生成树 Kruskal + 并查集
#include <iostream>
#include <cmath>
#define MAX 1001
using namespace std;
 
class tUFS
{
private:
 int F[MAX],Size;
 int findroot(int a)
 {
  int b=a,t;
  while (F[a]>0)
   a=F[a];
  while (F[b]>0)
  {
   t=F[b];
   F[b]=a;
   b=t;
  }
  return a;
 }
public:
 bool judge(int a,int b)
 {
  return F[findroot(a)]==F[findroot(b)];
 }
 void merge(int a,int b)
 {
  F[findroot(b)]=findroot(a);
 }
 tUFS(int N)
 {
  Size=N;
  for (int i=1;i<=N;i++)
   F[i]=-i;
 }
};
 
typedef struct
{
 int a,b;
 double v;
}edge;
 
typedef struct
{
 int x,y;
}point;
 
tUFS *U;
int N,M,C;
double Ans;
edge E[MAX*MAX];
point P[MAX];
 
void quicksort(int i,int j)
{
 int t1=i,t2=j;
 edge T,k=E[(i+j)/2];
 do
 {
  while (E[t1].v<k.v) t1++;
  while (E[t2].v>k.v) t2--;
  if (t1<=t2)
  {
   T=E[t1];
   E[t1]=E[t2];
   E[t2]=T;
 
   t1++;
   t2--;
  }
 }while (t1<t2);
 if (t2>i) quicksort(i,t2);
 if (t1<j) quicksort(t1,j);
}
 
inline double dist(point a,point b)
{
 return sqrt ( (double)(a.x-b.x)*(a.x-b.x) + (double)(a.y-b.y)*(a.y-b.y) );
}
 
void init()
{
 int i,j,a,b;
 freopen("roads.in","r",stdin);
 freopen("roads.out","w",stdout);
 scanf("%d%d",&N,&M);
 U=new tUFS(N);
 for (i=1;i<=N;i++)
  scanf("%d%d",&P[i].x,&P[i].y);
 for (i=1;i<=M;i++)
 {
  scanf("%d%d",&a,&b);
  if (!U->judge(a,b))
   U->merge(a,b);
 }
 for (i=C=1;i<=N-1;i++)
 {
  for (j=i+1;j<=N;j++,C++)
  {
   if (C==62375)
    C=C;
   E[C].a=i;
   E[C].b=j;
   E[C].v=dist(P[i],P[j]);
  }
 }
 C--;
 quicksort(1,C);
}
 
void kruskal()
{
 int i,cnt;
 for (i=1,cnt=0;i<=C && cnt<C-1-M;i++)
 {
  if (!U->judge(E[i].a,E[i].b))
  {
   U->merge(E[i].a,E[i].b);
   Ans+=E[i].v;
   cnt++;
  }
 }
}
 
int main()
{
 init();
 kruskal();
 printf("%.2lfn",Ans);
 return 0;
}
 
BYVoid大牛的代碼下載