owned this note
owned this note
Published
Linked with GitHub
# 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';
}
}
```
:::