1. 程式人生 > >BZOJ1023: [SHOI2008]cactus仙人掌圖(仙人掌)

BZOJ1023: [SHOI2008]cactus仙人掌圖(仙人掌)

sam content 怎麽 最長路 class .com col 單調隊列 重復

Description

  如果某個無向連通圖的任意一條邊至多只出現在一條簡單回路(simple cycle)裏,我們就稱這張圖為仙人掌
圖(cactus)。所謂簡單回路就是指在圖上不重復經過任何一個頂點的回路。

技術分享圖片

  舉例來說,上面的第一個例子是一張仙人圖,而第二個不是——註意到它有三條簡單回路:(4,3,2,1,6
,5,4)、(7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同時出現在前兩
個的簡單回路裏。另外,第三張圖也不是仙人圖,因為它並不是連通圖。顯然,仙人圖上的每條邊,或者是這張仙
人圖的橋(bridge),或者在且僅在一個簡單回路裏,兩者必居其一。定義在圖上兩點之間的距離為這兩點之間最
短路徑的距離。定義一個圖的直徑為這張圖相距最遠的兩個點的距離。現在我們假定仙人圖的每條邊的權值都是1
,你的任務是求出給定的仙人圖的直徑。

Input

  輸入的第一行包括兩個整數n和m(1≤n≤50000以及0≤m≤10000)。其中n代表頂點個數,我們約定圖中的頂
點將從1到n編號。接下來一共有m行。代表m條路徑。每行的開始有一個整數k(2≤k≤1000),代表在這條路徑上
的頂點個數。接下來是k個1到n之間的整數,分別對應了一個頂點,相鄰的頂點表示存在一條連接這兩個頂點的邊
。一條路徑上可能通過一個頂點好幾次,比如對於第一個樣例,第一條路徑從3經過8,又從8返回到了3,但是我們
保證所有的邊都會出現在某條路徑上,而且不會重復出現在兩條路徑上,或者在一條路徑上出現兩次。

Output

  只需輸出一個數,這個數表示仙人圖的直徑長度。

Sample Input

15 3
9 1 2 3 4 5 6 7 8 3
7 2 9 10 11 12 13 10
5 2 14 9 15 10 8

Sample Output

8
解題思路 第一次接觸仙人掌的蒟蒻QAQ 今天本來想學一下圓方樹,然而還沒學到怎麽建就卡在了這道題上。 這是一道仙人掌入門題,思路也比較樸素。
對於一顆仙人掌,我們最不容易處理的就是環,對於一個環,我們視為一個點雙連通分量。 所以我們使用tarjan 因為一個不在環內的點和環的頂點都可以直接更新答案。 答案用最長路徑+子節點最長路徑+1更新。 這個點的最長路徑用子節點最長路徑+1更新。 註意順序! 在一個環的頂點將整個環抽出(因為是一個環,所以抽出後復制一遍成為一個兩倍的鏈)
然後DP就可以啦 最長路徑可以被中間鏈+子節點最長路徑更新 這部分需要使用單調隊列維護 最後更新頂點即可^_^
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 using namespace std;
  5 struct pnt{
  6     int hd;
  7     int fa;
  8     int dfn;
  9     int low;
 10     int mxc;
 11 }p[1000000];
 12 struct ent{
 13     int twd;
 14     int lst;
 15 }e[1000000];
 16 int cnt;
 17 int n,m,k;
 18 int trc;
 19 int ans;
 20 int crt;
 21 int crl[1000000];
 22 int q[1000000];
 23 int h,t;
 24 void ade(int f,int t)
 25 {
 26     cnt++;
 27     e[cnt].twd=t;
 28     e[cnt].lst=p[f].hd;
 29     p[f].hd=cnt;
 30 }
 31 void ringbrk(int st,int fi)
 32 {
 33     crt=0;
 34     while(fi!=st)
 35     {
 36         crl[++crt]=p[fi].mxc;
 37         fi=p[fi].fa;
 38     }
 39     crl[++crt]=p[st].mxc;
 40     for(int i=1;i<crt;i++)
 41         crl[crt+i]=crl[i];
 42     h=t=1;
 43     q[1]=1;
 44     int ln=crt/2;
 45     for(int i=2;i<=crt+ln;i++)
 46     {
 47         while(h<=t&&i-q[h]>ln)
 48             h++;
 49         ans=max(ans,crl[q[h]]+crl[i]+i-q[h]);
 50         while(h<=t&&crl[q[t]]+i-q[t]<=crl[i])
 51             t--;
 52         q[++t]=i;
 53     }
 54     for(int i=1;i<crt;i++)
 55     {
 56         p[st].mxc=max(p[st].mxc,crl[i]+min(i,crt-i));
 57     }
 58 }
 59 void tarjan(int x)
 60 {
 61     p[x].dfn=p[x].low=++trc;
 62     for(int i=p[x].hd;i;i=e[i].lst)
 63     {
 64         int to=e[i].twd;
 65         if(to==p[x].fa)continue;
 66         if(!p[to].dfn)
 67         {
 68             p[to].fa=x;
 69             tarjan(to);
 70             p[x].low=min(p[x].low,p[to].low);
 71             if(p[x].dfn<p[to].low)
 72             {
 73                 ans=max(ans,p[x].mxc+p[to].mxc+1);
 74                 p[x].mxc=max(p[x].mxc,p[to].mxc+1);
 75             }
 76         }else{
 77             p[x].low=min(p[x].low,p[to].low);
 78         }
 79     }
 80     for(int i=p[x].hd;i;i=e[i].lst)
 81     {
 82         int to=e[i].twd;
 83         if(p[to].fa!=x&&p[to].dfn>p[x].dfn)
 84         {
 85             ringbrk(x,to);
 86         }
 87     }
 88 }
 89 int main()
 90 {
 91     scanf("%d%d",&n,&m);
 92     for(int i=1;i<=m;i++)
 93     {
 94         int frm,twd,nm;
 95         scanf("%d%d",&nm,&frm);
 96         for(int j=1;j<nm;j++)
 97         {
 98             scanf("%d",&twd);
 99             ade(twd,frm);
100             ade(frm,twd);
101             frm=twd;
102         }
103     }
104     tarjan(1);
105     printf("%d\n",ans);
106     return 0;
107 }

BZOJ1023: [SHOI2008]cactus仙人掌圖(仙人掌)