1. 程式人生 > >[SHOI2008]仙人掌圖 II——樹形dp與環形處理

[SHOI2008]仙人掌圖 II——樹形dp與環形處理

題意:

給定一個仙人掌,邊權為1

距離定義為兩個點之間的最短路徑

直徑定義為距離最遠的兩個點的距離

求仙人掌直徑

 

題解:

類比樹形dp求直徑。

f[i]表示i向下最多多長

處理鏈的話,直接dp即可。

處理環的話,類似點雙tarjan,把環上的點都拉出來。

先考慮拼接更新答案。斷環成鏈複製一倍,為了保證最短路,答案必須只能是f[i]+f[j]+i-j (i-len/2<=j<i)

單調佇列優化。

直接i-j即可,另一半的繞環會在複製後的那裡處理。

然後更新f[x],直接找環上其他的元素,距離就是兩段距離的較小值。

因為tarjan本質上是一棵dfs樹,所以處理環的時候元素都是x的兒子,兒子們的f必然已經處理。

 

tarjan點雙時注意:

 

 下面的寫法是錯誤的。

因為,點雙時的割點可能屬於多個dcc,所以可能y不和x緊挨著儲存。會彈出多餘的東西。

 

黑色是仙人掌,紅色是dfs樹。A,B是V-DCC

可能訪問x之後,先訪問了A,因為father的dfn小,所以不能彈棧。A的紅色部分在棧裡儲存。

然後從y進入,訪問B。發現訪問完了之後,可以彈棧,

如果是第二種寫法,那麼會等到棧頂是x才停止,那麼會把A中的點也彈出來。

根本知道彈出來的是什麼。。。。

第一種的話,會在彈出y之後停止。沒有問題。

 

癥結就因為x屬於兩個V-DCC

 

 

 

還要注意:

多次用queue,必須保證在l<=r時才能更新答案。

 

if(l<=r) ans=max(ans,f[mem[i]]+i+f[mem[q[l]]]-q[l]);

 

 

 

 程式碼:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long
ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=500000+5; int n,m; struct node{ int nxt,to; }e[10*N]; int hd[N],cnt; void add(int x,int y){ e[++cnt].nxt=hd[x]; e[cnt].to=y; hd[x]=cnt; } int dfn[N],df,low[N]; int f[N]; int q[N],l,r; int ans; int sta[N],top,len; int mem[2*N],num; void wrk(int x){ len=num; for(reg i=1;i<=num;++i) mem[i+num]=mem[i],ans=max(ans,f[mem[i]]); l=1,r=0; for(reg i=1;i<=2*num;++i){ while(l<=r&&q[l]<i-len/2) ++l; if(l<=r) ans=max(ans,f[mem[i]]+i+f[mem[q[l]]]-q[l]); while(l<=r&&f[mem[q[r]]]-q[r]<f[mem[i]]-i) --r; q[++r]=i; } for(reg i=2;i<=num;++i){ f[x]=max(f[x],f[mem[i]]+min(i-1,num+1-i)); } } void tarjan(int x){ //cout<<" tarjan "<<x<<" top "<<top<<endl; dfn[x]=low[x]=++df; sta[++top]=x; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); if(dfn[x]<=low[y]){//find V-dcc num=0; mem[++num]=x; int z; do{ z=sta[top--]; mem[++num]=z; }while(z!=y); wrk(x); } } else low[x]=min(low[x],dfn[y]); } } int main(){ rd(n);rd(m);int k,x,y; for(reg i=1;i<=m;++i){ rd(k);rd(x); for(reg j=1;j<k;++j){ rd(y);add(x,y);add(y,x);x=y; } } tarjan(1); // for(reg i=1;i<=n;++i){ // cout<<i<<" : "<<f[i]<<endl; // } printf("%d",ans); return 0; } } int main(){ // freopen("data.in","r",stdin); // freopen("my.out","w",stdout); Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/11/30 7:44:32 */