1. 程式人生 > >[NOIP2018]旅行(資料加強版)(圖論+基環樹)

[NOIP2018]旅行(資料加強版)(圖論+基環樹)

資料範圍多了2個0就是不一樣,O(n^2)只能68分了。(其中60分是n=m+1和原題一樣的做法送的),這題直接從NOIP難度變為NOI Plus難度了
不說廢話直接寫題解:
首先dfs一遍找到環,然後和n=m+1一樣從1號點訪問,然後跑到環以後,對於環上的點,可以執行僅一次返回到第一次到達環上的點的操作。所以dfs記錄的時候記錄下次訪問的點就可以,因為退回操作後只要退回途中下面有點,那麼就必須訪問,討論一下退回途中訪問的第1個點,比較其餘環上下一個點的大小就行了。注意dfs時要打標記(否則可能會有鍋)。

注意:資料有毒,要寫讀入優化+適當卡常數X過去(沒寫讀優被卡成96的我),最後執行時間最長的932ms通過,如果多交幾次發現還有沒通過的時候(不管了)

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+7;
int n,m,top,k,cnt,cut,a[N],b[N],cir[N],s[N],g[N],vis[N];
vector<int>v[N];
inline int read()
{
    int x=0,w=0;
    char ch=0;
    while(!isdigit(ch))w|=ch=='-',ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1
)+(ch^48),ch=getchar(); return w?-x:x; } void dfs(int u,int fa) { a[++top]=u; for(int i=0;i<v[u].size();++i)if(v[u][i]!=fa)dfs(v[u][i],u); } void getcir(int x,int fa) { s[++top]=x; vis[x]=1; int sz=v[x].size(); for(int i=0;i<sz;++i) if(v[x][i]!=fa) { if
(vis[v[x][i]]) { k=1; while(s[top]!=v[x][i])g[++cnt]=s[top--]; g[++cnt]=v[x][i]; return; } getcir(v[x][i],x); if(k)return; } vis[s[top--]]=0; } void dfs2(int x,int k,int fa) { a[++top]=x,vis[x]=1; int sz=v[x].size()-1; for(int i=0;i<=sz;++i)b[i]=v[x][i]; v[x].clear(); for(int i=0;i<=sz;++i) if(!vis[b[i]])v[x].push_back(b[i]); sz=v[x].size()-1; for(int i=0;i<=sz;++i) if(!vis[v[x][i]]) { if(i==sz&&cir[v[x][i]]&&!cut&&v[x][i]>k&&cir[fa]){cut=1;return;} if(i<sz-1)dfs2(v[x][i],v[x][i+1],x); else if(i==sz)dfs2(v[x][i],k,x); else{ int y=i+1; if(cir[v[x][y]]&&!cut&&v[x][y]>k&&cir[fa])dfs2(v[x][i],k,x); else dfs2(v[x][i],v[x][y],x); } } } int main() { scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++)v[x=read()].push_back(y=read()),v[y].push_back(x); for(int i=1;i<=n;i++)sort(v[i].begin(),v[i].end()); if(m==n-1) { dfs(1,0); for(int i=1;i<=n;i++)printf("%d ",a[i]); } else{ getcir(1,0); for(int i=1;i<=cnt;i++)cir[g[i]]=1; top=0; memset(vis,0,sizeof vis); dfs2(1,n+1,0); for(int i=1;i<=n;i++)printf("%d ",a[i]); } }
View Code