[洛谷P5049][洛谷P5022][題解]旅行
阿新 • • 發佈:2020-09-18
0.一些東西
原題
資料加強版
加強版程式碼參考你谷題解
終於調過了(又是一如既往的申必錯誤)
€€£ NOI plus石錘了
1.題解
原題的資料允許我們\(O(n^2)\)暴力斷邊,但是加強版的資料達到了\(n\log n\)級別,我們必須在斷邊這一環節尋求更好的解法。
考慮我們進入環後在何處回溯(根據繼續走環走到的點分類):
(設當前已經從\(b\)走到\(c\))
1.編號最小(\(d<e,f\)):顯然可以直接繼續走;
2.編號非最大/最小(\(e<d<f\)):按大小順序走完支鏈再來;
3.編號最大(\(d>e,f\)):需要分兩種情況討論,
①出邊就是最大的(\(d>b\)
②回溯後的比出邊大(\(b>d\)),只能硬著頭皮走下去。
所以說在走的時候需要記錄一下最大點,這時候把邊按照終點排序會很有幫助。
2.細節
1.只能回溯一次,注意標記(程式碼中back
);
2.注意一些功能的實現(如找環、排邊等等)。
3.程式碼
(附註釋)
(預設源在置頂部落格)
#define N 500010 int n,m,ans[N],tot,back,mx,isring; int vis[N],rin[N],fa[N]; struct Input { int frm,to; Input(int _u=0,int _v=0){ frm=_u,to=_v; } }ipt[N<<1]; bool operator < (Input a,Input b){ return a.to>b.to; } struct Edge { int to,nxt; }e[N<<1]; int head[N],cnt; inline void ade(int u,int v){ e[++cnt].to=v; e[cnt].nxt=head[u]; head[u]=cnt; } void DFS1(int now){//處理樹的情況 ans[++tot]=now,vis[now]=1; for(rg int i=head[now];i;i=e[i].nxt){ int v=e[i].to; if(!vis[v])DFS1(v); } } void DFS_Ring(int now,int ff){//找環 if(isring)return;//找到環的標記 if(!fa[now])fa[now]=ff; else if(fa[now]!=ff){//找到環了 while(ff!=now){//不斷回溯回去 rin[ff]=1; ff=fa[ff]; } rin[now]=isring=1; return; } for(rg int i=head[now];i;i=e[i].nxt){ int v=e[i].to; if(v!=ff)DFS_Ring(v,now); } } void DFS2(int now){//處理基環樹的情況 // cout<<"search "<<now<<endl; ans[++tot]=now,vis[now]=1; if(rin[now]){//在環上,特殊處理 int frog=0;//是否需要回溯(第三種情況) for(rg int i=head[now];i;i=e[i].nxt){ if(back)break;//不需要回溯了 int v=e[i].to; if(!vis[v]){ if(rin[v]){//終點在環上的出邊 i=e[i].nxt; while(vis[e[i].to])i=e[i].nxt;//不斷跳訪問過的 if(i)mx=e[i].to;//不是最大的出邊 else if(v>mx)frog=back=1;//是最大的,回溯 //此處運用到了排序後邊的順序 break; } } } for(rg int i=head[now];i;i=e[i].nxt){ int v=e[i].to; // printf("vis[%d]=%d,rin[%d]=%d,frog=%d\n",v,vis[v],v,rin[v],frog); if(!vis[v]){ if(!rin[v]||!frog)DFS2(v);//不需要回溯(注意||的使用) } } }else {//不在環上的正常走即可 for(rg int i=head[now];i;i=e[i].nxt){ int v=e[i].to; if(!vis[v])DFS2(v); } } } int main(){ Read(n),Read(m); for(rg int i=1;i<=m;i++){ int u,v; Read(u),Read(v); ipt[i]=Input(u,v),ipt[i+m]=Input(v,u);//先存一下 } sort(ipt+1,ipt+1+2*m); for(rg int i=1;i<=2*m;i++)ade(ipt[i].frm,ipt[i].to);//排序後加邊 if(m==n-1){ DFS1(1); for(rg int i=1;i<=n;i++)cout<<ans[i]<<" "; }else { DFS_Ring(1,1);/* for(rg int i=1;i<=n;i++){ printf("rin[%d]=%d\n",i,rin[i]); }*/ DFS2(1); for(rg int i=1;i<=n;i++)cout<<ans[i]<<" "; } return 0; }
4.完結撒花~
繼續咕咕咕