fatman87878
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee
  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    2
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # CSES GRAPH ALGORITHMS # [Counting Rooms](https://cses.fi/problemset/task/1192) 給你圖讓你判連通塊,對每個新遇到的點dfs和她連通的點,開vis維護每個位置是否被dfs到,$cnt$紀錄答案。 複雜度 : $O(V+E)=O(nm)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; vector<vector<bool>> vec; long long ans=0; vector<pair<int,int>> dir={{0,1},{1,0},{0,-1},{-1,0}}; inline void dfs(int y,int x){ vec[y][x]=true; for(int i = 0;i<4;i++){ if(vec[y+dir[i].first][x+dir[i].second])continue; dfs(y+dir[i].first,x+dir[i].second); } return ; } int main(){ cin.tie(nullptr)->sync_with_stdio(0);cout.tie(0); int n,m; char tmp; cin>>n>>m; vec.assign(n+2,vector<bool>(m+2,true)); for(int i = 1;i<=n;i++)for(int j = 1;j<=m;j++)cin>>tmp,vec[i][j]=(tmp=='#'); for(int i = 1;i<=n;i++)for(int j = 1;j<=m;j++)if(!vec[i][j])dfs(i,j),ans++; cout<<ans; } ``` ::: # [Labyrinth](https://cses.fi/problemset/task/1193) 直接bfs,vis過的不用走,紀錄從哪個點走過來。 複雜度 : $O(V+E)=O(nm)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int ax,ay,bx,by; int main(){ cin.tie(nullptr)->sync_with_stdio(0);cout.tie(0); pair<int,int> dir[4]={{1,0},{0,1},{-1,0},{0,-1}}; int n,m; cin>>n>>m; vector<vector<bool>> vec(n+2,vector<bool>(m+2,false)); char c; for(int i = 1;i<=n;i++){ for(int j = 1;j<=m;j++){ cin>>c; vec[i][j]=(c!='#'); if(c=='A')ay=i,ax=j; else if(c=='B')by=i,bx=j; } } vector<vector<int>> d(n+2,vector<int>(m+2,0)); d[ay][ax]=0; queue<pair<int,int>> q; q.push({ax,ay}); for(pair<int,int> tmp;!q.empty();){ tmp=q.front(),q.pop(); for(int i = 0;i<4;i++){ int nowx=tmp.first+dir[i].first,nowy=tmp.second+dir[i].second; if(!vec[nowy][nowx])continue; vec[nowy][nowx]=false; d[nowy][nowx]=i+1; q.push({nowx,nowy}); } } if(!d[by][bx]){ cout<<"NO"; return 0; } string s=""; char ans[4]={'R','D','L','U'}; while(bx!=ax||by!=ay){ s+=ans[d[by][bx]-1]; int tmpy=by-dir[d[by][bx]-1].second,tmpx=bx-dir[d[by][bx]-1].first; by=tmpy,bx=tmpx; } cout<<"YES"<<'\n'<<s.size()<<'\n'; for(int i = s.size()-1;i>=0;i--)cout<<s[i]; } ``` ::: # [Building Roads](https://cses.fi/problemset/task/1666) 給你一張圖,讓你把它變成聯通圖。 $dsu$判就好,最多只有n-1條邊要加,所以可以$O(n)$判。 複雜度 : $O(V \alpha (V))$ :::spoiler code: 這餘切的毒瘤扣 不要學 ```cpp= #include <bits/stdc++.h> #define F first #define S second using namespace std; using i64 = long long; using p2i = pair<int, int>; using p2l = pair<i64, i64>; struct DSU { int ___; vector<int> ____; DSU(int _) :___(_), ____(_ + 1, -1) {} inline int _________(int _) { return ____[_] < 0 ? _ : (____[_] = _________(____[_])); } inline void _____(int _, int __) { _ = _________(_), __ = _________(__); if (_ == __) return; if (____[_] > ____[__]) swap(_, __); ____[_] += ____[__]; ____[__] = _; --___; } }; int main() { cin.tie(nullptr)->sync_with_stdio(false); int n, m; cin >> n >> m; DSU _(n); for (int a, b; m--; ) { cin >> a >> b; _._____(a, b); } cout << _.___ - 1 << '\n'; vector<int> ans; for (int i = 1; i <= n; i++) if (_.____[i] < 0) ans.emplace_back(i); for (int i = 1; i < ans.size(); i++) cout << ans[0] << ' ' << ans[i] << '\n'; return 0; } ``` ::: # [Message Route](https://cses.fi/problemset/task/1667) 問你固定s和t的最短路,因為邊權相同,bfs直接做就好。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int main(){ int n,m; cin>>n>>m; vector<vector<int>> vec(n+1); vector<int> dist(n+1,0),dad(n+1,0),ans; vector<bool> chk(n+1,false); for(int a,b,i=0;i<m;i++)cin>>a>>b,vec[a].emplace_back(b),vec[b].emplace_back(a); queue<int> q; q.push(1); chk[1]=true; dist[1]=1; for(int tmp;!q.empty();){ tmp=q.front(),q.pop(); for(const int& i:vec[tmp]){ if(!chk[i]){ chk[i]=true; dist[i]=dist[tmp]+1; dad[i]=tmp; q.push(i); } } } if(!chk[n])return cout<<"IMPOSSIBLE",0; cout<<dist[n]<<'\n'; for(int i = 0,ed=n;i<dist[n];i++)ans.emplace_back(ed),ed=dad[ed]; for(int i = ans.size()-1;i>=0;i--)cout<<ans[i]<<' '; } ``` ::: # [Building Teams](https://cses.fi/problemset/task/1668) bfs著色,要是有人已經著過色了而且著的色和現在要給他著的色不同就燒雞。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); int n,m; cin>>n>>m; vector<vector<int>> vec(n+1); vector<int> team(n+1,0); vector<bool> chk(n+1,false); for(int a,b,i=0;i<m;i++)cin>>a>>b,vec[a].emplace_back(b),vec[b].emplace_back(a); queue<int> q; for(int i = 1;i<=n;i++){ if(!chk[i]){ team[i]=1; q.push(i); for(int tmp;!q.empty();){ tmp=q.front(),q.pop(); for(const int& i:vec[tmp]){ if(chk[i]&&team[i]==team[tmp])return cout<<"IMPOSSIBLE",0; if(!chk[i])q.push(i); chk[i]=true; team[i]=!(team[tmp]-1)+1; } } } } for(int i = 1;i<=n;i++)cout<<(team[i]?team[i]:1)<<' '; } ``` ::: # [Round Trip](https://cses.fi/problemset/task/1669/) 找一個環,輸出他。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); int n,m,ans; cin>>n>>m; vector<vector<int>> vec(n+1); vector<int> dad(n+1,0); vector<bool> chk(n+1,0); bool flag=false; for(int a,b,i=0;i<m;i++)cin>>a>>b,vec[a].emplace_back(b),vec[b].emplace_back(a); function<void(int,int)> dfs=[&](int now,int last)-> void { for(const int& i:vec[now]){ if(flag)return ; if(i==last)continue; dad[i]=now; if(chk[i]){ flag=true; ans=i; return; } chk[i]=true; dfs(i,now); chk[i]=false; } }; for(int i = 1;i<=n;i++)if(!dad[i])dfs(i,0); if(!flag)return cout<<"IMPOSSIBLE",0; vector<int> ret; for(int now=dad[ans];now!=ans;now=dad[now])ret.emplace_back(now); cout<<ret.size()+2<<'\n'<<ans<<' '; for(int i = ret.size()-1;i>=0;i--)cout<<ret[i]<<' '; cout<<ans; } ``` ::: # [Monsters](https://cses.fi/problemset/task/1194) 裸的多點源bfs,但@cot7302會,偏強。 複雜度 : $O(V+E)=O(nm)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; #define PII pair<int,int> #define F .first #define S .second int n,m,dis[1004][1004]; queue<PII> q; PII A,from[1004][1004]; inline void chk(const PII& p1,const PII& p2){if(dis[p2 F][p2 S]>dis[p1 F][p1 S]+1)dis[p2 F][p2 S]=dis[p1 F][p1 S]+1,q.push(p2),from[p2 F][p2 S]=p1;} bool possible=false; string s; void track(const PII& now){ PII p=from[now F][now S]; if(p==PII(0,0))return; if(p F==now F+1)s+="U"; else if(p F==now F-1)s+="D"; if(p S==now S+1)s+="L"; else if(p S==now S-1)s+="R"; track(p); } inline void bfs(bool which){ for(PII tmp,nxt;!q.empty();){ tmp=nxt=q.front(),q.pop(); nxt F++,chk(tmp,nxt),nxt F-=2,chk(tmp,nxt),nxt S++,nxt F++,chk(tmp,nxt),nxt S-=2,chk(tmp,nxt); if(which&&(tmp F==1||tmp F==n||tmp S==1||tmp S==m)){ possible=true; track(tmp); return ; } } } int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); char c; cin>>n>>m; for(int i = 1;i<=n;i++)for(int j = 1;j<=m;j++){ dis[i][j]=10000000; cin>>c; if(c=='#')dis[i][j]=0; if(c=='M')q.push({i,j}),dis[i][j]=0; else if(c=='A')A={i,j}; } bfs(0),q.push(A),dis[A F][A S]=0,from[A F][A S]={0,0},bfs(1); if(!possible){ cout<<"NO"; return 0; } cout<<"YES\n"<<s.size()<<'\n'; reverse(s.begin(),s.end()); cout<<s; } ``` ::: # [Shortest Routes I](https://cses.fi/problemset/task/1671) 單點源最短路,邊權全為正,直接dijkstra。 複雜度 : $O((V+E) \log (V+E))$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; typedef long long ll; struct edge{ ll pt,val; edge(){} edge(const ll& tpt,const ll& tval):pt(tpt),val(tval){} friend bool operator<(const edge& N1,const edge& N2){ return N1.val>N2.val; } }; int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); int n,m; cin>>n>>m; priority_queue<edge> pq; vector<vector<edge>> vec(n+1); vector<ll> dist(n+1,LLONG_MAX); dist[1]=0; vector<bool> visit(n+1,false); for(ll l,r,val;m--;){ cin>>l>>r>>val; vec[l].emplace_back(r,val); } pq.push(edge(1,0LL)); for(edge tmp;!pq.empty();){ tmp=pq.top(),pq.pop(); if(visit[tmp.pt])continue; visit[tmp.pt]=true; for(const edge& e:vec[tmp.pt]){ if(e.val+tmp.val>=dist[e.pt])continue; dist[e.pt]=e.val+tmp.val; pq.push(edge(e.pt,dist[e.pt])); } } for(int i = 1;i<=n;i++)cout<<dist[i]<<' '; } ``` ::: # [Shortest Routes II](https://cses.fi/problemset/task/1672) 多筆詢問,不同s和t求最短路,Floyd-Warshall模板題。 複雜度 : $O({n^3}+q)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; typedef long long ll; struct edge{ ll pt,val; edge(){} edge(const ll& tpt,const ll& tval):pt(tpt),val(tval){} friend bool operator<(const edge& a,const edge& b){ return a.val>b.val; } }; int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); int n,m,q; cin>>n>>m>>q; vector<vector<ll>> dist(n+1,vector<ll>(n+1,1e18)); for(int i = 1;i<=n;i++)dist[i][i]=0; for(ll a,b,val;m--;){ cin>>a>>b>>val; dist[a][b]=min(dist[a][b],val); dist[b][a]=dist[a][b]; } for(int k = 1;k<=n;k++){ for(int i = 1;i<=n;i++){ if(i==k)continue; for(int j = i+1;j<=n;j++){ if(j==k)continue; if(dist[i][j]>dist[i][k]+dist[k][j]){ dist[i][j]=dist[i][k]+dist[k][j]; dist[j][i]=dist[i][j]; } } } } for(int a,b;q--;){ cin>>a>>b; cout<<(dist[a][b]==1e18?-1:dist[a][b])<<'\n'; } } ``` ::: # [High Score](https://cses.fi/problemset/task/1673) 給你一張圖,邊權有正有負,求最長路。 先把邊權變號,再用bellman-ford求最短路,同時判有沒有負環。 複雜度 : $O(VE)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; typedef long long ll; int n,m; vector<ll> dis; vector<bool> vis1,vis2; vector<vector<int>> g1,g2; vector<pair<pair<int,int>,ll>> graph; void dfs1(int now){ if(vis1[now])return; vis1[now]=1; for(const auto& i:g1[now])dfs1(i); } void dfs2(int now){ if(vis2[now])return; vis2[now]=1; for(const auto& i:g2[now])dfs2(i); } void solve(){ cin>>n>>m; g1.assign(n+1,{}),g2.assign(n+1,{}),dis.assign(n+1,1e18),vis1.assign(n+1,false),vis2.assign(n+1,false); for(ll a,b,c;m--;)cin>>a>>b>>c,graph.push_back({{a,b},-c}),g1[a].emplace_back(b),g2[b].emplace_back(a); dfs1(1),dfs2(n); dis[1]=0LL; for(int i = 1;i<=n;i++){ for(const auto& j:graph){ if(dis[j.first.second]>dis[j.first.first]+j.second){ dis[j.first.second]=dis[j.first.first]+j.second; if(i==n&&vis1[j.first.second]&&vis2[j.first.second]){ cout<<-1; return; } } } } cout<<-dis[n]; } int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); solve(); } ``` ::: # [Flight Discount](https://cses.fi/problemset/task/1195) 給你一次機會把路徑中一個邊的$cost$減半(下高斯),求最短路。 dijkstra+dp。 轉移式 : $$ dp[flag][v] = flag?還沒折扣過,到v的dis : 折扣過,到v的dis $$ 答案就是$dp[1][n]$ 複雜度 : $O((V+E) \log (V+E))$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; typedef long long ll; int n,m; struct EDGE{ ll pt,val; bool used=false; EDGE(){} EDGE(const ll& tpt,const ll& tval,const bool& tu):pt(tpt),val(tval),used(tu){} friend bool operator<(const EDGE& a,const EDGE& b){ return a.val>b.val; } }; int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); cin>>n>>m; vector<vector<EDGE>> vec(n+1); vector<vector<ll>> dis(n+1,{LLONG_MAX,LLONG_MAX}); dis[1]={0,0}; vector<bool> vis(n+1,false); for(ll a,b,c;m--;)cin>>a>>b>>c,vec[a].emplace_back(EDGE(b,c,0)); priority_queue<EDGE> pq; pq.push(EDGE(1,0,0)); for(EDGE tmp;!pq.empty();){ tmp=pq.top(),pq.pop(); if(dis[tmp.pt][tmp.used]!=tmp.val)continue; for(const EDGE& e:vec[tmp.pt]){ if(!tmp.used){ if(dis[tmp.pt][0]+e.val/2<dis[e.pt][1]){ dis[e.pt][1]=dis[tmp.pt][0]+e.val/2; pq.push(EDGE(e.pt,dis[e.pt][1],1)); } } if(dis[tmp.pt][tmp.used]+e.val<dis[e.pt][tmp.used]){ dis[e.pt][tmp.used]=dis[tmp.pt][tmp.used]+e.val; pq.push(EDGE(e.pt,dis[e.pt][tmp.used],tmp.used)); } } } cout<<dis[n][1]; } ``` ::: # [Cycle Finding](https://cses.fi/problemset/task/1197) 給你圖,問你有沒有負環,那不就||Bellman-Ford||。 複雜度 : $O(VE)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; typedef long long ll; int n,m; vector<tuple<int,int,int>> graph; void solve(){ cin>>n>>m; graph.assign(n+1,{}); for(ll a,b,c;m--;)cin>>a>>b>>c,graph.emplace_back(a,b,c); vector<ll> dist(n+1,1e18),ancest(n+1,0); int flag=0; for(int i = 1;i<=n;i++){ flag=0; for(const auto& [l,r,val]:graph){ if(dist[l]+val<dist[r]){ dist[r]=dist[l]+val; ancest[r]=l; flag=r; } } } if(!flag){ cout<<"NO\n"; return; } vector<int> ans; cout<<"YES\n"; for(int i = 0;i<n;i++)flag=ancest[flag]; for(int i = ancest[flag];;i=ancest[i]){ ans.emplace_back(i); if(i==flag)break; } for(int i = ans.size();i;i--)cout<<ans[i-1]<<' '; cout<<flag<<' '; return; } int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); solve(); } ``` ::: # [Flight Routes](https://cses.fi/problemset/task/1196) 跟[Flight Discount](https://cses.fi/problemset/task/1195)一樣的概念,然後第一個狀態改成「第幾短路」,答案就是$dp[k][n]$ 實作時推薦用$multiset$維護dp陣列。 複雜度 : $O(K(V+E)( \log (V+E)+ \log K))$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; #define ll long long struct EDGE{ ll r,val; EDGE(){} EDGE(const ll& R,const ll& v):r(R),val(v){} friend bool operator<(const EDGE& a,const EDGE& b){ return a.val>b.val; } }; int n,m,k; vector<EDGE> vec[1<<17]; vector<multiset<ll>> dis(1<<17); int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); cin>>n>>m>>k; for(ll i=0,l,r,v;i<m;i++){ cin>>l>>r>>v; vec[l].emplace_back(EDGE(r,v)); } priority_queue<EDGE> pq; pq.push(EDGE(1,0)),dis[1].insert(0); for(EDGE tmp;!pq.empty();){ tmp=pq.top(),pq.pop(); if(tmp.val>*--dis[tmp.r].end())continue; for(const EDGE& e:vec[tmp.r]){ if(dis[e.r].size()<k){ dis[e.r].insert(tmp.val+e.val); pq.push(EDGE(e.r,tmp.val+e.val)); } else if(e.val+tmp.val<*--dis[e.r].end()){ dis[e.r].erase(--dis[e.r].end()); dis[e.r].insert(e.val+tmp.val); pq.push(EDGE(e.r,e.val+tmp.val)); } } } int j = 0; for(const ll& i:dis[n]){ cout<<i<<' '; if(++j==k)return 0; } } ``` ::: # [Round Trip II](https://cses.fi/problemset/task/1678) 跟[Round Trip](https://cses.fi/problemset/task/1669/)不同的只有他是有向邊,但判法其實一樣||有點不知道這題到底有甚麼存在的必要||。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; constexpr int maxN=2e5+5; int n,m,lst[maxN]; vector<int> graph[maxN]; bitset<maxN> vis,went; int dfs(int now){ if(vis[now])return now; if(went[now])return 0; vis[now]=1,went[now]=1; for(int i:graph[now]){ lst[i]=now; int k=dfs(i); if(k)return k; } vis[now]=0; return 0; } int main(){ cin.tie(nullptr)->sync_with_stdio(0); cin>>n>>m; for(int a,b;m--;)cin>>a>>b,graph[a].emplace_back(b); vector<int> ans; for(int i = 1,s=0;i<=n;i++){ if(!went[i])s=dfs(i); if(s){ for(int i = lst[s];i!=s;i=lst[i])ans.emplace_back(i); cout<<ans.size()+2<<'\n'<<s<<' '; for(int i = ans.size()-1;i>=0;i--)cout<<ans[i]<<' '; return cout<<s,0; } } cout<<"IMPOSSIBLE"; } ``` ::: # [Course Schedule](https://cses.fi/problemset/task/1679) 完成$v$前要完成$u$,等同於建一條$u \to v$的有向邊,而能完成$v$的條件就是要讓全部連到$v$的人都做過,可以拓樸排序。 不可行的條件就是有點沒有被拓排到。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int n,m; vector<int> graph[1<<17],deg(1<<17),order; queue<int> q; int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); cin>>n>>m; for(int i = 0,a,b;i<m;i++){ cin>>a>>b; deg[b]++; graph[a].emplace_back(b); } for(int i = 1;i<=n;i++)if(!deg[i])q.push(i),order.emplace_back(i); for(int tmp;!q.empty();){ tmp=q.front(),q.pop(); for(const auto& i:graph[tmp]){ deg[i]--; if(!deg[i])q.push(i),order.emplace_back(i); } } if(order.size()<n){ cout<<"IMPOSSIBLE"; return 0; } for(const int& i:order)cout<<i<<' '; } ``` ::: # [Longest Flight Route](https://cses.fi/problemset/task/1680) 拓樸排序為每個點紀錄從前面走來最長的路徑,答案就是$n$的答案。 注意判斷每條路徑是否是從$1$出發的。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define all(x) x.begin(),x.end() #define I int #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=1e5+5; I n,m,nxt[maxN],dp[maxN],indeg[maxN]; vector<I> adj[maxN],ans; queue<pii> q; I main(){ IOS cin>>n>>m; for(I a,b;m--;)cin>>a>>b,adj[a].emplace_back(b),indeg[b]++; rep(i,1,n+1)if(!indeg[i])q.emplace(i,i==1); for(pii tmp;!q.empty();){ tmp=q.front(),q.pop(); for(I i:adj[tmp F]){ if(tmp S&&dp[i]<dp[tmp F]+1)dp[i]=dp[tmp F]+1,nxt[i]=tmp F; if(!--indeg[i])q.emplace(i,!!dp[i]||i==1); } } if(!dp[n])return cout<<"IMPOSSIBLE",0; cout<<dp[n]+1<<"\n1 "; for(;n!=1;n=nxt[n])ans.emplace_back(n); for(;!ans.empty();ans.pop_back())cout<<ans.back()<<' '; } ``` ::: 另解 : 存反圖對$n$做dfs,然後記憶化搜索。 小知識 : 這是我這題去年第一次做的時候的原始做法。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define all(x) x.begin(),x.end() #define I int #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=1e5+5; I n,m,nxt[maxN]; pii dp[maxN]; bitset<maxN> vis; vector<I> adj[maxN],topo; pii dfs(I now){ if(now==1)return {1,1}; if(vis[now])return dp[now]; vis[now]=1; for(I i:adj[now]){ pii tmp=dfs(i); if(tmp S)tmp F++; if(tmp F>dp[now] F)swap(tmp,dp[now]),nxt[now]=i; } return dp[now]; } I main(){ IOS cin>>n>>m; for(I a,b;m--;)cin>>a>>b,adj[b].emplace_back(a); dfs(n); if(!dp[n] S)return cout<<"IMPOSSIBLE",0; cout<<dp[n] F<<"\n1 "; for(;n!=1;n=nxt[n])topo.emplace_back(n); for(;!topo.empty();topo.pop_back())cout<<topo.back()<<' '; } ``` ::: # [Game Routes](https://cses.fi/problemset/task/1681) 簡單拓排dp。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define all(x) x.begin(),x.end() #define I int #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=1e5+5,mod=1e9+7; I n,m,dp[maxN],indeg[maxN]; vector<I> adj[maxN]; queue<I> q; inline void add(I& a,I b){ if((a+=b)>=mod)a-=mod; } I main(){ IOS cin>>n>>m; for(I a,b;m--;)cin>>a>>b,indeg[adj[a].emplace_back(b)]++; dp[1]=1; rep(i,1,n+1)if(!indeg[i])q.emplace(i); for(I tmp;!q.empty();){ tmp=q.front(),q.pop(); for(I i:adj[tmp]){ add(dp[i],dp[tmp]); if(!--indeg[i])q.emplace(i); } } cout<<dp[n]; } ``` ::: # [Investigation](https://cses.fi/problemset/task/1202) 四個東西可以在一次dijkstra裡維護好。 推薦寫一個struct當作每個點的dp值,可以很好維護。 :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define all(x) x.begin(),x.end() #define I int #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=1e5+5,mod=1e9+7; int n,m; struct EDGE{ ll r,val; EDGE(){} EDGE(const ll& a,const ll& b):r(a),val(b){} friend bool operator<(const EDGE& a,const EDGE& b){ return a.val>b.val; } }; struct node{ ll minnode,maxnode,all,dis; }dis[maxN]; vector<EDGE> adj[maxN]; priority_queue<EDGE> pq; inline void add(I& a,I b){ if((a+=b)>=mod)a-=mod; } I main(){ cin.tie(nullptr)->sync_with_stdio(0); cin>>n>>m; for(ll l,r,v;m--;){ cin>>l>>r>>v; adj[l].emplace_back(EDGE(r,v)); } rep(i,1,n+1)dis[i].dis=INF; pq.push(EDGE(1,0)); dis[1].all=1; for(EDGE tmp;!pq.empty();){ tmp=pq.top(),pq.pop(); if(tmp.r==n)continue; for(const EDGE& e:adj[tmp.r]){ if(dis[e.r].dis>tmp.val+e.val){ dis[e.r].maxnode=0,dis[e.r].minnode=INF,dis[e.r].all=0; pq.push(EDGE(e.r,dis[e.r].dis=tmp.val+e.val)); } if(dis[e.r].dis==tmp.val+e.val){ dis[e.r].maxnode=max(dis[e.r].maxnode,dis[tmp.r].maxnode+1); dis[e.r].minnode=min(dis[e.r].minnode,dis[tmp.r].minnode+1); dis[e.r].all=(dis[e.r].all+dis[tmp.r].all)%(ll)(1e9+7); } } } cout<<dis[n].dis<<' '<<dis[n].all<<' '<<dis[n].minnode<<' '<<dis[n].maxnode; } ``` ::: # [Planets Queries I](https://cses.fi/problemset/task/1750) 給一張functional graph,$q$個queries求從$v$跳$k$步會到哪。 每個點出度=1,看到$k$的值域,稍微~~通靈~~思考一下可以發現能用倍增。 複雜度 : $O(n \log C+q \log k)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int n,q,grid[30][1<<18]; inline int jp(int pos,int d){ for(int i=0;d;d>>=1,i++)if(d&1)pos=grid[i][pos]; return pos; } int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); cin>>n>>q; for(int i = 1;i<=n;i++)cin>>grid[0][i]; for(int i = 1;i<30;i++)for(int j = 1;j<=n;j++)grid[i][j]=grid[i-1][grid[i-1][j]]; for(int a,b;q--;){ cin>>a>>b; cout<<jp(a,b)<<'\n'; } } ``` ::: # [Planets Queries II](https://cses.fi/problemset/task/1160) 給你functional graph,$q$筆詢問問$a$點到$b$點的距離。 先輩知識 : functional graph出來的結果會是一個以上的水母圖 可以先分case來討論。 ``` 1.a和b在不同連通塊 : 無解。 2.a和b在水母旁邊的同一棵樹上 : 若b是a的祖先,答案就會是深度差,反之則無解。 3.a和b在水母旁邊的不同棵樹上 : 無解。 4.a在樹上,b在環上 : 先把a走到環上,再走到b即可。 5.a在環上,b在樹上 : 無解。 6.a和b在環上 : 同4 ``` 1用$dsu$判。 $2$和$3$的區別是根的不同,所以可以對每個樹上的點紀錄誰是他們的根,而$2$你可以用 (1)倍增(複雜度$O(\log V)$)(2)歐拉序(複雜度$O(1)$)來判,我這份是用歐拉序 $4$和$5$單純紀錄在不在環上,這可以用拓排解決,而$4$可以幫環上每個人編號,並記錄好該環的大小,可以$O(1)$求答案。 剩下就剩實作細節了,我用$SCC$幫環編號,但好像其實沒必要。 :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define I int #define A auto #define C char #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=2e5+5; I n,q,cnt,tmp,scc[maxN],root[maxN],edge[maxN],dep[maxN],dsu[maxN],indeg[maxN],circlesize[maxN],in[maxN],out[maxN]; inline I find(I pos){ return dsu[pos]<0?pos:(dsu[pos]=find(dsu[pos])); } inline void merge(I a,I b){ a=find(a),b=find(b); if(a==b)return ; if(dsu[a]<dsu[b])swap(a,b); dsu[b]+=dsu[a],dsu[a]=b; } vector<I> adj[maxN],order,egde[maxN]; bitset<maxN> vis,notcircle; queue<I> topo; void dfs1(I now){ vis[now]=1; if(!vis[edge[now]])dfs1(edge[now]); order.emplace_back(now); } void dfs2(I now,I id){ scc[now]=id; for(I i:egde[now])if(!scc[i])dfs2(i,id); } void tree(I now,I last,I rt){ in[now]=++cnt; if(now!=last)vis[now]=1; dep[now]=dep[last]+1; root[now]=rt; for(I i:adj[now])tree(i,now,rt); out[now]=cnt; } int main(){ IOS cin>>n>>q; fill(dsu,dsu+n+1,-1); rep(i,1,n+1)cin>>tmp,edge[i]=tmp,egde[tmp].emplace_back(i),indeg[tmp]++,merge(tmp,i); rep(i,1,n+1)if(!vis[i])dfs1(i); reverse(order.begin(),order.end()); for(I i:order)if(!scc[i])dfs2(i,i); rep(i,1,n+1)if(!indeg[i])topo.emplace(i); while(!topo.empty()){ tmp=topo.front(),topo.pop(); notcircle[tmp]=1; I i = edge[tmp]; adj[i].emplace_back(tmp); if(!(--indeg[i]))topo.emplace(i); } vis.reset(); rep(i,1,n+1)if(!notcircle[i])tree(i,i,i); rep(i,1,n+1)if(!vis[scc[i]]){ vis[scc[i]]=1; for(I pos=scc[i],j=1;pos!=scc[i]||j;pos=edge[pos],j=0)circlesize[edge[pos]]=circlesize[pos]+1; } for(I a,b,dis;q--;){ cin>>a>>b; if(find(a)!=find(b))cout<<"-1\n"; else if(!notcircle[b]){ dis=circlesize[b]-circlesize[root[a]]+circlesize[scc[b]]; if(dis>=circlesize[scc[b]])dis-=circlesize[scc[b]]; cout<<dis+dep[a]-1<<'\n'; } else if(in[b]<=in[a]&&out[b]>=out[a]){ cout<<dep[a]-dep[b]<<'\n'; } else cout<<"-1\n"; } } ``` ::: # [Planets Cycles](https://cses.fi/problemset/task/1751) 又是functional graph,又要找環,所以我 又去寫$SCC$了,hehe。 詳細作法 : 存反圖,把$SCC$找出來後縮點。 對每個連通塊從環上dfs下去,每一層答案就會是`dep+環的大小`。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define all(x) x.begin(),x.end() #define I int #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=2e5+5; I n,adj[maxN],scc[maxN],cnt[maxN],ans[maxN]; vector<I> jda[maxN],G[maxN],order; bitset<maxN> vis,rundfs; void dfs1(I now){ vis[now]=1; if(!vis[adj[now]])dfs1(adj[now]); order.emplace_back(now); } void dfs2(I now,I id){ scc[now]=id; cnt[id]++; for(I i:jda[now])if(!scc[i])dfs2(i,id); } void dfs(I now,I val){ vis[now]=1,ans[now]=val; for(I i:G[now])dfs(i,val+1); } I main(){ IOS cin>>n; rep(i,1,n+1)cin>>adj[i],jda[adj[i]].emplace_back(i); rep(i,1,n+1)if(!vis[i])dfs1(i); reverse(order.begin(),order.end()); for(I i:order)if(!scc[i])dfs2(i,i); rep(i,1,n+1)for(I j:jda[i])if(scc[i]!=scc[j])G[scc[i]].emplace_back(scc[j]); vis.reset(); rep(i,1,n+1)if(!vis[scc[i]]&&(cnt[scc[i]]>1||adj[i]==i))dfs(scc[i],cnt[scc[i]]); rep(i,1,n+1)cout<<ans[scc[i]]<<' '; } ``` ::: :::spoiler 另解: 可以發現他是functional graph,所以他會是很多環,旁邊會有很多往環裡戳的樹,所以只要拓排就可以找出所有環。 只要再進一步DFS就可以求出旁邊樹上點的深(離開環的方向)度,環上的點答案就會是環的大小,樹上的點答案就會是深度$+$往上跳跳到的環的大小。 時間複雜度:$O(V+E)$。 :::spoiler code: ```cpp= #include <bits/stdc++.h> using namespace std; bitset<200001> cycle; int indeg[200001], reach[200001], depth[200001], root[200001], cycle_size[200001]; vector<int> G[200001]; void dfs(int x, int rt) { root[x] = rt; for (auto to : G[x]) depth[to] = depth[x] + 1, dfs(to, rt); } int main() { cin.tie(nullptr)->sync_with_stdio(false); int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> reach[i]; ++indeg[reach[i]]; } { queue<int> Q; for (int i = 1; i <= n; i++) if (indeg[i] == 0) Q.emplace(i); for (; !Q.empty(); ) { int x = Q.front(); Q.pop(); G[reach[x]].emplace_back(x); if (--indeg[reach[x]] == 0) Q.emplace(reach[x]); } } for (int i = 1; i <= n; i++) cycle[i] = !!indeg[i]; for (int i = 1; i <= n; i++) if (cycle[i]) dfs(i, i); for (int i = 1; i <= n; i++) if (indeg[i]) { for (int x = i; indeg[x]; ) { indeg[x] = 0; root[x] = i; cycle_size[i]++; x = reach[x]; } } for (int i = 1; i <= n; i++) cout << (cycle[i] ? cycle_size[root[i]] : depth[i] + cycle_size[root[root[i]]]) << ' '; return 0; } ``` ::: # [Road Reparation](https://cses.fi/problemset/task/1675) 要讓整張圖聯通的話至少要建出一棵樹,題目要求邊權和最小,所以求最小生成樹。 時間複雜度:$O((V+E) \log (V+E))$(Prim)。 :::spoiler code: ```cpp= #include <iostream> #include <algorithm> #include <ranges> #include <queue> #include <vector> #include <utility> #include <functional> using namespace std; constexpr long inf = 1e18; long dist[100001]; bool vis[100001]; vector<pair<int, int>> G[100001]; int N, M; int main() { cin >> N >> M; for (int a, b, w; M--; ) { cin >> a >> b >> w; G[a].emplace_back(b, w); G[b].emplace_back(a, w); } long ans = 0; ranges::fill(dist + 2, dist + N + 1, inf); priority_queue<pair<long, int>, vector<pair<long, int>>, greater<pair<long, int>>> pq; pq.emplace(0, 1); int cnt = 0; for (; !pq.empty(); ) { auto [d, x] = pq.top(); pq.pop(); if (vis[x]) continue; vis[x] = 1; ans += d; ++cnt; for (auto [to, w] : G[x]) if (w < dist[to]) { pq.emplace(dist[to] = w, to); } } if (cnt != N) return cout << "IMPOSSIBLE", 0; cout << ans; } ``` ::: # [Road Construction](https://cses.fi/problemset/task/1676) 動態維護聯通塊且只有加邊$\to$ 併查集。 時間複雜度:$O(n + m\alpha (n))$。 :::spoiler code: ```cpp= #include <iostream> using namespace std; struct disjoint_set { int* _; int _count, max_cc_size; disjoint_set(int L) :_(new int[L + 1]), _count(L), max_cc_size(1) { fill(_ + 1, _ + L + 1, -1); } ~disjoint_set() { delete[] _; } int root(int x) { return _[x] < 0 ? x : (_[x] = root(_[x])); } int count() { return _count; } void merge(int a, int b) { a = root(a), b = root(b); if (a == b) return; if (_[a] >_[b]) swap(a, b); _[a] += _[b]; max_cc_size = max(max_cc_size, -_[a]); _[b] = a; --_count; } }; int N, M; int main() { cin >> N >> M; disjoint_set D(N); for (int a, b; M--; ) { cin >> a >> b; D.merge(a, b); cout << D.count() << ' ' << D.max_cc_size << '\n'; } } ``` ::: # [Flight Routes Check](https://cses.fi/problemset/task/1682) 題目問的是所有點是否互相可達$\to$整張圖是否只有一個$SCC$。 做一次縮$SCC$之後,如果有兩塊以上的$SCC$,構解只要遍歷每條邊,如果有一條$u \to v$的邊且$u,v$在不同的SCC裡面,就輸出`u v`,不然就亂找兩個在不同SCC的節點就好。 時間複雜度:$O(V+E)$。 :::spoiler code: ```cpp= #include <iostream> #include <vector> #include <bitset> using namespace std; bitset<100001> vis; vector<int> G[100001], rG[100001]; int n, m, dfs_order[100001], scc_id[100001], scc_cnt; void dfs(int x) { static int order = 0; vis[x] = true; for (auto to : G[x]) if (!vis[to]) dfs(to); dfs_order[order++] = x; } void sfd(int x) { scc_id[x] = scc_cnt; for (auto to : rG[x]) if (!scc_id[to]) sfd(to); } int main() { cin >> n >> m; for (int a, b; m--; ) { cin >> a >> b; G[a].emplace_back(b); rG[b].emplace_back(a); } for (int i = 1; i <= n; i++) if (!vis[i]) dfs(i); for (int i = n; i--; ) if (!scc_id[dfs_order[i]]) ++scc_cnt, sfd(dfs_order[i]); if (scc_cnt == 1) return cout << "YES", 0; cout << "NO\n"; for (int i = 1; i <= n; i++) { for (auto to : G[i]) if (scc_id[to] != scc_id[i]) return cout << to << ' ' << i, 0; } for (int i = 2; i <= n; i++) { if (scc_id[1] != scc_id[i]) return cout << 1 << ' ' << i, 0; } __builtin_unreachable(); } ``` ::: :::spoiler 另解: 用kosaraju的想法,對隨便一個點做兩次$dfs$,一次從正圖走,一次從反圖走,如果有人不能走到那就會是答案。 複雜度 : $O(V+E)$ 不吃毒聲明:我沒有吃毒用$SCC$,前面是魚切寫的 :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int n,m; bool vis[1<<17]; vector<int> vec[1<<17][2]; void dfs(int now,bool k){for(const auto& i:vec[now][k])if(!vis[i])vis[i]=1,dfs(i,k);} int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); cin>>n>>m; for(int a,b;m--;){ cin>>a>>b; vec[a][0].emplace_back(b); vec[b][1].emplace_back(a); } dfs(1,0); for(int i = 2;i<=n;i++)if(!vis[i])return cout<<"NO"<<'\n'<<1<<' '<<i,0; memset(vis,0,sizeof(vis)); dfs(1,1); for(int i = 2;i<=n;i++)if(!vis[i])return cout<<"NO"<<'\n'<<i<<' '<<1,0; cout<<"YES"; } ``` ::: # [Planets and Kingdoms](https://cses.fi/problemset/task/1683) 臻的是$SCC$了。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int n,m,cnt; vector<int> graph[1<<17],hparg[1<<17],vis(1<<17),topo,scc(1<<17),ans(1<<17); void dfs(int now){ vis[now]=1; for(const int& i:graph[now])if(!vis[i])dfs(i); topo.emplace_back(now); } void sfd(int now,int ind){ scc[now]=ind; for(const int& i:hparg[now])if(!scc[i])sfd(i,ind); } int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); cin>>n>>m; for(int a,b;m--;)cin>>a>>b,graph[a].emplace_back(b),hparg[b].emplace_back(a); for(int i = 1;i<=n;i++)if(!vis[i])dfs(i); for(auto it=topo.rbegin();it!=topo.rend();it++)if(!scc[*it])sfd(*it,*it); for(int i = 1;i<=n;i++)if(!ans[scc[i]])ans[scc[i]]=++cnt; cout<<cnt<<'\n'; for(int i = 1;i<=n;i++)cout<<ans[scc[i]]<<' '; } ``` ::: # [Giant Pizza](https://cses.fi/problemset/task/1684) 每個人的條件至少有一個要達成,可以用$2-SAT$解決。 具體連邊的方法 :$$ u \to v \ 代表如果 \ u=false \ 時,v要是true $$ 可以用擴展域的想法解決,最後只要判每個東西是否矛盾就好。 而這個是用$SCC$判,對於同一個條件如果兩個節點在同一個$SCC$就代表兩種條件$(true,false)$會在同一個case裡。 :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; int n,m,cnt; char c1,c2; vector<int> graph[1<<18],hparg[1<<18],topo,scc(1<<18); bitset<(1<<18)> vis,ans; void dfs(int now){ vis[now]=1; for(const int& i:graph[now])if(!vis[i])dfs(i); topo.emplace_back(now); } inline void sfd(int now){ scc[now]=cnt; for(const int& i:hparg[now])if(!scc[i])sfd(i); } int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); cin>>n>>m; for(int i = 0,a,b;i<n;i++){ cin>>c1>>a>>c2>>b; if(c1=='-')a=2*m-a+1; if(c2=='-')b=2*m-b+1; graph[2*m-a+1].emplace_back(b),graph[2*m-b+1].emplace_back(a); hparg[a].emplace_back(2*m-b+1),hparg[b].emplace_back(2*m-a+1); } for(int i = 1;i<=2*m;i++)if(!vis[i])dfs(i); for(auto it=topo.rbegin();it!=topo.rend();it++)if(!scc[*it])++cnt,sfd(*it); for(int i = 1;i<=m;i++){ if(scc[i]==scc[2*m-i+1])return cout<<"IMPOSSIBLE",0; ans[i]=(scc[i]>scc[2*m-i+1]); } for(int i = 1;i<=m;i++)cout<<(ans[i]?"+ ":"- "); } ``` ::: # [Coin Collector](https://cses.fi/problemset/task/1686) 給你一張有向圖,每走過一個點,你可以拿走他上面全部的錢,最大化拿到的錢。 發現對於每個環,最佳解就是把整個環拿完,所以最佳解就會是縮完$SCC$後拓排$dp$。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops,inline") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define ll long long #define pii pair<int,int> #define lb(x) (x)&-(x) #define F .first #define S .second #define rep(i,l,r) for(int i = l;i<r;i++) #define ld long double #define pdd pair<ld,ld> #define INF 5e18 constexpr int maxN=1e5+1; int n,m; bitset<maxN> vis; vector<int> adj[maxN],jda[maxN],graph[maxN],order; ll scc[maxN],deg[maxN],val[maxN],dp[maxN]; queue<int> q; void dfs1(int now){ vis[now]=1; for(int& i:adj[now])if(!vis[i])dfs1(i); order.emplace_back(now); } void dfs2(int now,int id){ scc[now]=id; for(int& i:jda[now])if(!scc[i])dfs2(i,id); } int main(){ IOS cin>>n>>m; rep(i,1,n+1)cin>>val[i]; for(int a,b;m--;)cin>>a>>b,jda[adj[a].emplace_back(b)].emplace_back(a); rep(i,1,n+1)if(!vis[i])dfs1(i); vis.reset(); for(auto i=order.rbegin();i!=order.rend();i++)if(!scc[*i])dfs2(*i,*i); rep(i,1,n+1)if(i!=scc[i])val[scc[i]]+=val[i]; rep(i,1,n+1)for(int& j:jda[i])if(scc[j]!=scc[i])graph[scc[i]].emplace_back(scc[j]),deg[scc[j]]++; rep(i,1,n+1)if(!deg[scc[i]]&&!vis[scc[i]])q.push(scc[i]),vis[scc[i]]=1,dp[scc[i]]=val[scc[i]]; for(int tmp;!q.empty();){ tmp=q.front(),q.pop(); for(int& i:graph[tmp]){ dp[i]=max(dp[i],dp[tmp]+val[i]); if(!(--deg[i]))q.push(i); } } cout<<*max_element(dp+1,dp+n+1); } ``` ::: # [Mail Delivery](https://cses.fi/problemset/task/1691) 找歐拉迴路。 判度數,如果有人度數是奇數那就絕對沒有解。 構解的方式是你直接跑,紀錄後序$dfs$序,最後$reverse$就好了。 :::spoiler 前序和後序的差別 : ```= 隨便構一個長的像豬尾巴(中間有一個環)的圖,再連一條邊從尾巴到頭,然後你去模擬看看前序和後序的差別。 你會發現前序要是先戳到本來應該是最後走的那條路就會燒雞,但後序不會。 因為後序存的是反的歐拉迴路,第一個丟進去的一定是走到死路,也就是本來最後走的那條。 而剩下的順序就不太重要,後序就是好的了。 ``` ::: 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops,inline") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define I int #define A auto #define C char #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=1e5+5; I n,m,cnt[maxN],boss[maxN],in[maxN]; vector<pii> adj[maxN]; vector<I> order; bitset<maxN*2> vis; void dfs(I now){ for(int pos,id;!adj[now].empty();){ tie(pos,id)=adj[now].back(),adj[now].pop_back(); if(!vis[id])vis[id]=1,dfs(pos); } order.emplace_back(now); } inline I find(I pos){ return boss[pos]==pos?pos:(boss[pos]=find(boss[pos])); } inline void merge(I a,I b){ a=find(a),b=find(b); if(a!=b)boss[b]=a; } int main(){ IOS cin>>n>>m; iota(boss,boss+n+1,0); for(I a,b,i=0;i<m;i++)cin>>a>>b,adj[a].emplace_back(b,i),adj[b].emplace_back(a,i),in[a]++,in[b]++; if(!in[1])return cout<<"IMPOSSIBLE",0; rep(i,1,n+1)if(in[i]&1)return cout<<"IMPOSSIBLE",0; dfs(1); if(order.size()!=m+1)return cout<<"IMPOSSIBLE",0; reverse(order.begin(),order.end()); for(I& i:order)cout<<i<<' '; } ``` ::: # [De Bruijn Sequence](https://cses.fi/problemset/task/1692) [de Bruijn Sequence (Graph Theory)](https://www.youtube.com/watch?v=VZvU1_oPjg0) 我是看這個懂的,這個有拓展到$k$進制的De Bruijn Sequence,但應該大同小異。 複雜度 : $O({2^n})$ :::spoiler code: ```cpp= #include <iostream> #include <vector> #include <bitset> #include <utility> #include <tuple> #include <algorithm> using namespace std; bitset<65536> vis; vector<int> G[65536], ans; int N, mask; bool dfs(int x) { vis[x] = true; ans.emplace_back(x); if (ssize(ans) == (1 << N)) return true; for (auto to : G[x]) if (!vis[to]) { if (dfs(to)) return true; } vis[x] = false; return ans.pop_back(), false; } string to_binary(int x, int len) { string ret; for (; len--; x >>= 1) ret += (x & 1) ^ '0'; ranges::reverse(ret); return ret; } int main() { cin >> N; mask = (1 << N) - 1; int edge_count = 0; for (int i = 0; i <= mask; i++) { int u = (i & (mask >> 1)) << 1, v = u | 1; if (u != i) G[i].emplace_back(u); if (v != i) G[i].emplace_back(v); } dfs(0); cout << to_binary(ans[0], N); for (int i = 1; i < ssize(ans); i++) cout << (ans[i] & 1); } ``` ::: # [Teleporters Path](https://cses.fi/problemset/task/1693) 有向圖的歐拉路徑,和歐拉迴路的差別只有$s$的出度和$t$的入度要是奇數,剩下的人出入度要同。 複雜度 : $O(V+E)$ :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define I int #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=1e5+5; int n,m,deg[maxN]; vector<pii> adj[maxN]; vector<int> order; bitset<maxN<<1> vis; void dfs(int now){ for(pii tmp;!adj[now].empty();)tmp=adj[now].back(),adj[now].pop_back(),dfs(tmp F); order.emplace_back(now); } int main(){ IOS cin>>n>>m; for(int a,b,i=0;i<m;i++)cin>>a>>b,deg[a]--,deg[b]++,adj[a].emplace_back(b,i); if(deg[1]!=-1||deg[n]!=1)return cout<<"IMPOSSIBLE",0; rep(i,2,n)if(deg[i])return cout<<"IMPOSSIBLE",0; dfs(1); if(order.size()!=m+1)return cout<<"IMPOSSIBLE",0; for(int i = order.size();i--;)cout<<order[i]<<' '; } ``` ::: # [Hamiltonian Flights](https://cses.fi/problemset/task/1690/) 給你一張圖,求他的漢密爾頓路徑數。 可以位元dp,狀態及轉移式就是$$ dp_{mask,i} = 你已經走過mask的每一個點,且最後在i $$$$ dp_{mask,i} = \sum_{j \in mask \ and \ j \ != \ i} dp_{mask \oplus {2^i},j} $$ 然後如果沒寫好會tle。 (4/20)學長對不起我海旅的時候在亂寫轉移式 複雜度 : $O({V^2}{2^V})$ :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; #define ll long long constexpr ll mod=1e9+7; ll n,m,graph[21][21],dp[1<<20|1][21]; int main(){ cin.tie(nullptr)->sync_with_stdio(0),cout.tie(0); cin>>n>>m; for(int a,b;m--;)cin>>a>>b,graph[a-1][b-1]++; dp[1][0]=1; for(int i = 1;i<(1<<n)-1;i++)for(int j = 0;j<n;j++) if((1<<j)&i) for(int k = 0;k<n;k++)if(!((1<<k)&i)) (dp[(1<<k)|i][k]+=dp[i][j]*graph[j][k])%=mod; cout<<dp[(1<<n)-1][n-1]; } ``` ::: # [Knight's Tour](https://cses.fi/problemset/task/1689) 給你$8 \times 8$的棋盤和起始點,要你構造出一組漢米爾頓路徑。 詭譎題,想了很久去看題解後發現是唬爛,把你可以走下去的點選出來,然後按照他們能走下去的點的數量由小到大dfs下去,然後就過了,還特別快。 其實感覺是能走的點的數量會越走越少,然後就很好。 複雜度 : $O(magic)$ :::spoiler code: ```cpp= #pragma GCC optimize("O3,unroll-loops") //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt") #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define all(x) x.begin(),x.end() #define I int #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=1e5+5; constexpr I fx[8]={-2,-2,-1,-1,1,1,2,2},fy[8]={-1,1,-2,2,-2,2,-1,1}; vector<pii> adj[10][10]; I ans[10][10]; inline I chk(I x,I y){ return x>0&&x<9&&y>0&&y<9&&!ans[x][y]; } inline I cnt(I x,I y){ I ret=0; rep(i,0,8)ret+=chk(x+fx[i],y+fy[i]); return ret; } bool dfs(I x,I y,I cur=1){ ans[x][y]=cur; if(cur==64)return 1; vector<tuple<I,I,I>> go; rep(i,0,8){ I tox=x+fx[i],toy=y+fy[i]; if(chk(tox,toy))go.push_back({cnt(tox,toy),tox,toy}); } sort(all(go)); for(auto& [d,tox,toy]:go)if(dfs(tox,toy,cur+1))return 1; ans[x][y]=0; return 0; } int main(){ int n,m; cin>>m>>n; dfs(n,m); rep(i,1,9)rep(j,1,9)cout<<ans[i][j]<<" \n"[j==8]; } ``` ::: # [Download Speed](https://cses.fi/problemset/task/1694/) 裸的網路最大流。 時間複雜度:$O(V^2\ E)$。 :::spoiler code: ```cpp= #include <bits/stdc++.h> using namespace std; using i64 = long long; struct dinic { static constexpr inline int inf = 1e9; struct edge { int to; i64 cap, flow; }; int n, m; vector<edge> edges; vector<vector<int>> G; vector<int> dis, id; dinic(int __n, int __m) : n(__n), m(__m), edges(m << 1), G(n + 1), dis(n + 1, 0), id(n + 1, 0) { for (int i = 0; i < m; i++) { int a, b, c; cin >> a >> b >> c; edges[i << 1] = {b, c, 0}; edges[i << 1 | 1] = {a, 0, 0}; G[a].emplace_back(i << 1); G[b].emplace_back(i << 1 | 1); } } bool bfs(int s, int t) { ranges::fill(dis, inf); queue<int> q; dis[q.emplace(s)] = 0; while (!q.empty()) { int x = q.front(); q.pop(); for (auto eid : G[x]) { auto& [to, cap, flow] = edges[eid]; if (dis[to] == inf && flow != cap) dis[q.emplace(to)] = dis[x] + 1; } } return dis[t] != inf; } i64 dfs(int x, int t, i64 f) { if (x == t || f == 0) return f; i64 ret = 0; for (int& i = id[x]; i < ssize(G[x]); i++) { auto& [to, cap, flow] = edges[G[x][i]]; if (dis[to] != dis[x] + 1) continue; i64 f_to = dfs(to, t, min(f - ret, cap - flow)); if (f_to > 0) { flow += f_to; edges[G[x][i] ^ 1].flow -= f_to; ret += f_to; if (ret == f) return ret; } } return ret; } i64 operator()(int s, int t) { i64 flow = 0; while (1) { if (!bfs(s, t)) break; ranges::fill(id, 0); while (1) { i64 f = dfs(s, t, 1e18); if (f <= 0) break; flow += f; } } return flow; } }; int main() { int n, m; cin >> n >> m; cout << dinic{n, m}(1, n) << '\n'; } ``` ::: # [Police Chase](https://cses.fi/problemset/task/1695) 單位網路最小$S-T$割。因為[最大流最小割定理](https://oi-wiki.org/graph/flow/max-flow/#%E6%9C%80%E5%A4%A7%E6%B5%81%E6%9C%80%E5%B0%8F%E5%89%B2%E5%AE%9A%E7%90%86),所以先跑一次最大流之後,在殘量網路中從原點開始DFS走還有剩餘流量的邊,這樣可以分出兩個點集,之後遍歷每一條邊,兩端在不同點集的就是割邊。 時間複雜度:$O(\min(V^{\frac{2}{3}}, E^{\frac{1}{2}})\ E)$。 :::spoiler code: ```cpp= #include <bits/stdc++.h> using namespace std; using i64 = long long; struct dinic { struct edge { int to, rc; }; static constexpr inline int inf = 1e9; int n; vector<edge> edges; vector<vector<int>> G; vector<int> dist, id; dinic(int __n) : n(__n), edges(), G(n), dist(n), id(n) {} void add_edge(int from, int to, int cap) { G[from].emplace_back(ssize(edges)); edges.emplace_back(to, cap); G[to].emplace_back(ssize(edges)); edges.emplace_back(from, cap); } bool bfs(int s, int t) { ranges::fill(dist, inf); queue<int> q; dist[q.emplace(s)] = 0; while (!q.empty()) { auto x = q.front(); q.pop(); for (auto eid : G[x]) { auto [to, rc] = edges[eid]; if (dist[to] == inf && rc > 0) dist[q.emplace(to)] = dist[x] + 1; } } return dist[t] != inf; } int dfs(int x, int t, int f) { if (x == t || f == 0) return f; int ret = 0; for (int& i = id[x]; i < ssize(G[x]); i++) { auto& [to, rc] = edges[G[x][i]]; if (dist[to] != dist[x] + 1) continue; int f_to = dfs(to, t, min(f - ret, rc)); if (f_to > 0) { rc -= f_to; edges[G[x][i] ^ 1].rc += f_to; ret += f_to; if (ret == f) return ret; } } return ret; } int operator()(int s, int t) { int flow = 0; while (bfs(s, t)) { ranges::fill(id, 0); while (1) { int f = dfs(s, t, inf); if (f <= 0) break; flow += f; } } return flow; } vector<pair<int, int>> min_cut(int s, int t) { this->operator()(s, t); vector<bool> vis(n, false); vector<pair<int, int>> ret; auto f = [&](auto&& f, int x) -> void { vis[x] = true; for (auto eid : G[x]) { auto [to, rc] = edges[eid]; if (!vis[to] && rc > 0) { f(f, to); } } }; f(f, s); vis[t] = false; for (int i = 0; i < n; i++) if (vis[i]) { for (auto eid : G[i]) { auto [to, rc] = edges[eid]; if (!vis[to]) ret.emplace_back(i, to); } } return ret; } }; int main() { int n, m; cin >> n >> m; dinic Dinic(n + 1); for (int a, b; m--; ) { cin >> a >> b; Dinic.add_edge(a, b, 1); } auto ret = Dinic.min_cut(1, n); cout << ssize(ret) << '\n'; for (auto [a, b] : ret) cout << a << ' ' << b << '\n'; } ``` ::: # [School Dance](https://cses.fi/problemset/task/1696) 裸的二分圖匹配。可以用flow做,時間複雜度是$O(\sqrt{V}\ E)$,但是常數比較大。 :::spoiler code(Dinic): ```cpp= #include <bits/stdc++.h> using namespace std; using i64 = long long; struct dinic { struct edge { int to, rc; }; static constexpr inline int inf = 1e9; int n; vector<edge> edges; vector<vector<int>> G; vector<int> dist, id; dinic(int __n) : n(__n), edges(), G(n), dist(n), id(n) {} void add_edge(int from, int to, int cap) { G[from].emplace_back(ssize(edges)); edges.emplace_back(to, cap); G[to].emplace_back(ssize(edges)); edges.emplace_back(from, 0); } bool bfs(int s, int t) { ranges::fill(dist, inf); queue<int> q; dist[q.emplace(s)] = 0; while (!q.empty()) { auto x = q.front(); q.pop(); for (auto eid : G[x]) { auto [to, rc] = edges[eid]; if (dist[to] == inf && rc > 0) dist[q.emplace(to)] = dist[x] + 1; } } return dist[t] != inf; } int dfs(int x, int t, int f) { if (x == t || f == 0) return f; int ret = 0; for (int& i = id[x]; i < ssize(G[x]); i++) { auto& [to, rc] = edges[G[x][i]]; if (dist[to] != dist[x] + 1) continue; int f_to = dfs(to, t, min(f - ret, rc)); if (f_to > 0) { rc -= f_to; edges[G[x][i] ^ 1].rc += f_to; ret += f_to; if (ret == f) return ret; } } return ret; } int operator()(int s, int t) { int flow = 0; while (bfs(s, t)) { ranges::fill(id, 0); while (1) { int f = dfs(s, t, inf); if (f <= 0) break; flow += f; } } return flow; } }; int main() { int n, m, k; cin >> n >> m >> k; dinic Dinic(1 + n + m + 1); for (int i = 1; i <= n; i++) { Dinic.add_edge(0, i, 1); } for (int i = n + 1; i <= n + m; i++) { Dinic.add_edge(i, n + m + 1, 1); } for (int a, b; k--; ) { cin >> a >> b; Dinic.add_edge(a, b + n, 1); } cout << Dinic(0, n + m + 1) << '\n'; for (int i = 1; i <= n; i++) { for (auto eid : Dinic.G[i]) { auto [to, rc] = Dinic.edges[eid]; if (rc == 0 && to > n) cout << i << ' ' << to - n << '\n'; } } } ``` ::: 或是寫Hopcroft-Karp,複雜度一樣。 :::spoiler code(Hopcroft-Karp): ```cpp= #include <bits/stdc++.h> using namespace std; struct Hopcroft_Karp { static constexpr inline int inf = 1e9; int nx, ny, dis; vector<vector<int>> G; vector<int> dx, dy, xlink, ylink; vector<bool> vis; Hopcroft_Karp(int __x, int __y) : nx(__x), ny(__y), G(__x + 1), dx(__x + 1), dy(__y + 1), xlink(__x + 1), ylink(__y + 1), vis(__y + 1) {} void add_edge(int u, int v) { G[u].emplace_back(v); } bool bfs() { fill(begin(dx), end(dx), inf); fill(begin(dy), end(dy), inf); dis = inf; queue<int> q; for (int i = 1; i <= nx; i++) if (!xlink[i]) dx[q.emplace(i)] = 0; while (!q.empty()) { int u = q.front(); q.pop(); for (auto to : G[u]) if (dy[to] == inf) { dy[to] = dx[u] + 1; if (!ylink[to]) dis = dy[to]; else dx[q.emplace(ylink[to])] = dy[to] + 1; } } return dis != inf; } bool dfs(int u) { for (auto to : G[u]) if (!vis[to] && dy[to] == dx[u] + 1) { vis[to] = true; if (ylink[to] && dy[to] == dis) continue; if (!ylink[to] || dfs(ylink[to])) { xlink[u] = to, ylink[to] = u; return true; } } return false; } int operator()() { int ret = 0; while (bfs()) { fill(begin(vis), end(vis), false); for (int i = 1; i <= nx; i++) if (!xlink[i]) ret += dfs(i); } return ret; } }; int main() { cin.tie(nullptr)->sync_with_stdio(false); int n, m, k; cin >> n >> m >> k; Hopcroft_Karp HK(n, m); for (int a, b; k--; ) { cin >> a >> b; HK.add_edge(a, b); } cout << HK() << '\n'; for (int i = 1; i <= n; i++) if (HK.xlink[i]) cout << i << ' ' << HK.xlink[i] << '\n'; } ``` ::: 當然可以正常寫二分圖匹配。 :::spoiler code: ```cpp= #include<bits/stdc++.h> using namespace std; #define IOS cin.tie(nullptr)->sync_with_stdio(0),cin.exceptions(cin.failbit); #define lb(x) (x)&-(x) #define INF 5e18 #define all(x) x.begin(),x.end() #define I int #define ll long long #define pii pair<I,I> #define pll pair<ll,ll> #define F .first #define S .second #define ld long double #define pdd pair<ld,ld> #define rep(i,l,r) for(I i = l;i<r;i++) constexpr I maxN=5e2+5; I n,m,k; pii match[maxN]; vector<pii> adj[maxN]; vector<pii> ans; bitset<maxN*2> vis,use; bool dfs(I now){ for(pii i:adj[now])if(!vis[i F]){ vis[i]=1; if(!match[i F] F||dfs(match[i F] F)) return use[match[i F] S]=0,match[i F]={now,i S},use[i S]=1,1; } return 0; } I main(){ IOS cin>>n>>m>>k; for(I a,b;k--;)cin>>a>>b,adj[a].emplace_back(b,k+1); rep(i,1,n+1)dfs(i),vis.reset(); rep(i,1,n+1)for(pii j:adj[i])if(use[j S])ans.emplace_back(i,j F); cout<<ans.size()<<'\n'; rep(i,0,ans.size())cout<<ans[i] F<<' '<<ans[i] S<<'\n'; } ``` ::: # [Distinct Routes](https://cses.fi/problemset/task/1711) 要找出最多條從$1$出發,$n$結束的路徑,且這些路徑除了$1$和$n$以外經過的點不能重複。 稍微觀察一下就能發現跑一遍$1 \to n$的最大流就是答案了。 時間複雜度:$O(V^2\ E)$。 :::spoiler code: ```cpp= #include <bits/stdc++.h> using namespace std; using i64 = long long; struct dinic { struct edge { int to, rc; }; static constexpr inline int inf = 1e9; int n; vector<edge> edges; vector<vector<int>> G; vector<int> dist, id; dinic(int __n) : n(__n), edges(), G(n), dist(n), id(n) {} void add_edge(int from, int to, int cap) { G[from].emplace_back(ssize(edges)); edges.emplace_back(to, cap); G[to].emplace_back(ssize(edges)); edges.emplace_back(from, 0); } bool bfs(int s, int t) { ranges::fill(dist, inf); queue<int> q; dist[q.emplace(s)] = 0; while (!q.empty()) { auto x = q.front(); q.pop(); for (auto eid : G[x]) { auto [to, rc] = edges[eid]; if (dist[to] == inf && rc > 0) dist[q.emplace(to)] = dist[x] + 1; } } return dist[t] != inf; } int dfs(int x, int t, int f) { if (x == t || f == 0) return f; int ret = 0; for (int& i = id[x]; i < ssize(G[x]); i++) { auto& [to, rc] = edges[G[x][i]]; if (dist[to] != dist[x] + 1) continue; int f_to = dfs(to, t, min(f - ret, rc)); if (f_to > 0) { rc -= f_to; edges[G[x][i] ^ 1].rc += f_to; ret += f_to; if (ret == f) return ret; } } return ret; } int operator()(int s, int t) { int flow = 0; while (bfs(s, t)) { ranges::fill(id, 0); while (1) { int f = dfs(s, t, inf); if (f <= 0) break; flow += f; } } return flow; } vector<vector<int>> distinct_routes(int s, int t) { this->operator()(s, t); vector<bool> vis(size(edges), false); vector<int> tmp(1, s); vector<vector<int>> ret; auto f = [&](auto&& f, int x) -> bool { tmp.emplace_back(x); for (auto eid : G[x]) if (!(eid & 1) && !vis[eid]) { auto [to, rc] = edges[eid]; if (rc == 0) vis[eid] = true; else continue; if (to == t && rc == 0) { tmp.emplace_back(t); return true; } if (rc == 0) if (f(f, to)) { return true; } } tmp.pop_back(); return false; }; for (auto eid : G[s]) if (!(eid & 1)) { auto [to, rc] = edges[eid]; if (to == t && rc == 0) { ret.emplace_back(vector{s, t}); continue; } if (rc == 0) { if (f(f, to)) { ret.emplace_back(tmp); tmp.clear(); tmp.emplace_back(s); continue; } } } return ret; } }; int main() { int n, m; cin >> n >> m; dinic Dinic(n + 1); for (int a, b; m--; ) { cin >> a >> b; Dinic.add_edge(a, b, 1); } auto ret = Dinic.distinct_routes(1, n); cout << size(ret) << '\n'; for (auto& v : ret) { cout << size(v) << '\n'; for (auto x : v) cout << x << ' '; cout << '\n'; } } ``` :::

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully