[SHOI2008]仙人掌圖 II——樹形dp與環形處理
阿新 • • 發佈:2018-11-30
題意:
給定一個仙人掌,邊權為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 longll; 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 */