11 条题解
-
0
CF1213F Unstable String Sort
#include <bits/stdc++.h> using namespace std; const int MAXN = 200000; int n, k; int p[MAXN + 5]; int q[MAXN + 5]; // 按 p[] 标记的每个点的顺序 int id[MAXN + 5]; // 反向边并查集 int fa[MAXN + 5]; int findFa(int x) { if (x == fa[x]) return x; return fa[x] = findFa(fa[x]); } int main() { ios::sync_with_stdio(false); cin.tie(0); // 输入 cin >> n >> k; for (int i = 1; i <= n; i++) cin >> p[i]; for (int i = 1; i <= n; i++) cin >> q[i]; // 按 p[] 标顺序 for (int i = 1; i <= n; i++) id[p[i]] = i; /* for (int i = 1; i <= n; i++) cout << id[i] << " "; cout << "\n"; */ // 检查 q 的逆序边来缩点 for (int i = 1; i <= n; i++) fa[i] = i; for (int i = 1; i <= n - 1; i++) { // a[q[i]] <= a[q[i+1]] int u = q[i]; int v = q[i + 1]; int faU = findFa(u); int faV = findFa(v); // 把 v ~ u 一连串并起来 if (id[v] < id[u] && faU != faV) { int fvid = id[faV]; int fuid = id[faU]; // 按id // id[faV] ~ id[v], ,id[faU]/fuid ~ id[u] // id[faV] ~ id[v], ,id[faPre]~preId,id[faU]/fuid ~ id[u] // 按 p 里面的顺序的每个数 // faV ~ v, ,faPre ~ pre ,faU ~ u while (fuid != fvid) { int preId = fuid - 1; // p[preId] = pre int faPre = findFa(p[preId]); fa[faU] = faPre; faU = faPre; fuid = id[faPre]; } } } // 复用 id[] 存缩点后的排名 1~ id[p[1]] = 1; int rnk = 1; for (int i = 2; i <= n; i++) { // 开缩 if (findFa(p[i]) != p[i]) id[p[i]] = id[p[i - 1]]; else id[p[i]] = ++rnk; } if (rnk < k) { cout << "NO\n"; return 0; } cout << "YES\n"; for (int i = 1; i <= n; i++) if (id[i] < k) cout << (char)('a' + id[i] - 1); else cout << (char)('a' + k - 1); return 0; }
-
0
P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G
#include <bits/stdc++.h> using namespace std; const int MAXN = 100000; int n, m; vector<int> edge[MAXN + 5]; bool vis[MAXN + 5], inS[MAXN + 5]; int low[MAXN + 5], num[MAXN + 5], belong[MAXN + 5], out[MAXN + 5]; int ind, resCnt; stack<int> s; void dfs(int u) { vis[u] = true; low[u] = num[u] = ++ind; s.push(u); inS[u] = true; for (int i = 0; i < edge[u].size(); i++) { int v = edge[u][i]; //u->v; if (!vis[v]) { dfs(v); low[u] = min(low[u], low[v]); } else if (inS[v]) { low[u] = min(low[u], num[v]); } } if (num[u] == low[u]) { ++resCnt; while (s.top() != u) { belong[s.top()] = resCnt; inS[s.top()] = false; s.pop(); } belong[u] = resCnt; inS[u] = false; s.pop(); } } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; edge[u].push_back(v); } for (int i = 1; i <= n; i++) { if (!vis[i]) dfs(i); } for (int u = 1; u <= n; u++) { for (int i = 0; i < edge[u].size(); i++) { int v = edge[u][i]; if (belong[u] != belong[v]) { out[belong[u]]++; } } } int zero = -1; for (int i = 1; i <= resCnt; i++) { if (out[i] == 0) { if (zero == -1) { zero = i; } else { cout << "0"; return 0; } } } int ans = 0; for (int i = 1; i <= n; i++) if (belong[i] == zero) ans++; cout << ans << endl; return 0; }
-
0
P4306 [JSOI2010]连通数
Floyd 传递闭包
#include <bits/stdc++.h> using namespace std; int n; string s; bitset<2005> e[2005]; int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i = 0; i < n; i++) { cin >> s; for (int j = 0; j < s.length(); j++) if (s[j] == '1') e[i].set(j); e[i].set(i); } for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) if (e[j][i]) e[j] |= e[i]; int ans = 0; for (int i = 0; i < n; i++) ans += e[i].count(); cout << ans << "\n"; return 0; }
缩点后拓扑排序dp
TLE
#include<bits/stdc++.h> using namespace std; const int MAXN = 2000; int n; char g[MAXN+5][MAXN+5]; vector<int> e[MAXN+5]; //原图 vector<int> ee[MAXN+5]; //缩点后的新图 vector<int> points;//新图中的点的编号 //缩点相关 int idx;//时间戳 int dfn[MAXN+5],low[MAXN+5]; stack<int> s; bool inS[MAXN+5]; int belong[MAXN+5];//原图每个点属于缩点后的哪个点 int cnt[MAXN+5];//缩点后的每个新点包括了几个原来的点 //拓扑排序 int d[MAXN+5];//存缩点后的每个点反向图度数 queue<int> q; //求答案 int ans;//最终答案 bool flag[MAXN+5][MAXN+5];//存缩点后的新图的两点间是否相连 void dfs(int u){ dfn[u] = low[u] = ++idx; s.push(u); inS[u] = true; for(int v:e[u]){ if(dfn[v]==0){ dfs(v); low[u] = min(low[u], low[v]); }else if(inS[v]){ low[u] = min(low[u], dfn[v]); } } if(dfn[u]==low[u]){ //栈里面u以及u后面进去的点都属于根为u的强联通分量 while(s.top()!=u){ cnt[u]++; belong[s.top()]=u; inS[s.top()]=false; s.pop(); } cnt[u]++; belong[u]=u; inS[u]=false; s.pop(); } } int main(){ ios::sync_with_stdio(false); cin.tie(0); //读取原图 cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>g[i][j]; for(int u=1;u<=n;u++){ for(int v=1;v<=n;v++){ if(g[u][v]=='1') e[u].push_back(v); } } //原图的强连通分量缩点 for(int i=1;i<=n;i++) if(dfn[i]==0) dfs(i); /* for(int i=1;i<=n;i++) cout<<belong[i]<<" "<<cnt[i]<<endl; */ //构建新图(反图) for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) if(g[i][j]=='1') if(belong[i]!=belong[j]){ ee[belong[j]].push_back(belong[i]); d[belong[i]]++; //cout<<belong[i]<<"->"<<belong[j]<<endl; } if(belong[i]==i) points.push_back(i); } //新图是一个dag,拓扑排序的过程中求解新图中的点连接关系 for(int u:points) if(d[u]==0) q.push(u); while(q.empty()==false){ int u=q.front(); q.pop(); //cout<<u<<endl; for(int v:ee[u]){ //u能到哪些点,v就也能到那些点 for(int x:points) if(flag[u][x]) flag[v][x]=true; flag[v][u]=true; //拓扑排序 d[v]--; if(d[v]==0){ q.push(v); } } } //统计答案 ans=0; for(int u:points){ ans+=cnt[u]*cnt[u]; for(int v:points){ if(flag[u][v]==true) ans+=cnt[u]*cnt[v]; } } cout<<ans<<"\n"; return 0; }
AC
#include<bits/stdc++.h> using namespace std; const int MAXN = 2000; int n; char g[MAXN+5][MAXN+5]; vector<int> e[MAXN+5]; //原图 vector<int> ee[MAXN+5]; //缩点后的新图 vector<int> points;//新图中的点的编号 //缩点相关 int idx;//时间戳 int dfn[MAXN+5],low[MAXN+5]; stack<int> s; bool inS[MAXN+5]; int belong[MAXN+5];//原图每个点属于缩点后的哪个点 int cnt[MAXN+5];//缩点后的每个新点包括了几个原来的点 //拓扑排序 int d[MAXN+5];//存缩点后的每个点反向图度数 queue<int> q; //求答案 int ans;//最终答案 bitset<MAXN+5> flag[MAXN+5];//存缩点后的新图的两点间是否相连 void dfs(int u){ dfn[u] = low[u] = ++idx; s.push(u); inS[u] = true; for(int v:e[u]){ if(dfn[v]==0){ dfs(v); low[u] = min(low[u], low[v]); }else if(inS[v]){ low[u] = min(low[u], dfn[v]); } } if(dfn[u]==low[u]){ //栈里面u以及u后面进去的点都属于根为u的强联通分量 while(s.top()!=u){ cnt[u]++; belong[s.top()]=u; inS[s.top()]=false; s.pop(); } cnt[u]++; belong[u]=u; inS[u]=false; s.pop(); } } int main(){ ios::sync_with_stdio(false); cin.tie(0); //读取原图 cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>g[i][j]; for(int u=1;u<=n;u++){ for(int v=1;v<=n;v++){ if(g[u][v]=='1') e[u].push_back(v); } } //原图的强连通分量缩点 for(int i=1;i<=n;i++) if(dfn[i]==0) dfs(i); /* for(int i=1;i<=n;i++) cout<<belong[i]<<" "<<cnt[i]<<endl; */ //构建新图(反图) for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) if(g[i][j]=='1') if(belong[i]!=belong[j]){ ee[belong[j]].push_back(belong[i]); d[belong[i]]++; //cout<<belong[i]<<"->"<<belong[j]<<endl; } if(belong[i]==i) points.push_back(i); } //新图是一个dag,拓扑排序的过程中求解新图中的点连接关系 for(int u:points) if(d[u]==0) q.push(u); while(q.empty()==false){ int u=q.front(); q.pop(); //cout<<u<<endl; for(int v:ee[u]){ //u能到哪些点,v就也能到那些点 flag[v] = flag[v]|flag[u]; flag[v].set(u); //拓扑排序 d[v]--; if(d[v]==0){ q.push(v); } } } //统计答案 ans=0; for(int u:points){ ans+=cnt[u]*cnt[u]; for(int v:points){ if(flag[u][v]==true) ans+=cnt[u]*cnt[v]; } } cout<<ans<<"\n"; return 0; }
缩点后记忆化搜索dp
#include<bits/stdc++.h> using namespace std; const int MAXN = 2000; int n; char g[MAXN+5][MAXN+5]; vector<int> e[MAXN+5]; //原图 vector<int> ee[MAXN+5]; //缩点后的新图 vector<int> points;//新图中的点的编号 //缩点相关 int idx;//时间戳 int dfn[MAXN+5],low[MAXN+5]; stack<int> s; bool inS[MAXN+5]; int belong[MAXN+5];//原图每个点属于缩点后的哪个点 int cnt[MAXN+5];//缩点后的每个新点包括了几个原来的点 //求答案 int ans;//最终答案 bool vis[MAXN+5];//标记每个店的flag有没有求完 bitset<MAXN+5> flag[MAXN+5];//存缩点后的新图的两点间是否相连 void dfs(int u){ dfn[u] = low[u] = ++idx; s.push(u); inS[u] = true; for(int v:e[u]){ if(dfn[v]==0){ dfs(v); low[u] = min(low[u], low[v]); }else if(inS[v]){ low[u] = min(low[u], dfn[v]); } } if(dfn[u]==low[u]){ //栈里面u以及u后面进去的点都属于根为u的强联通分量 while(s.top()!=u){ cnt[u]++; belong[s.top()]=u; inS[s.top()]=false; s.pop(); } cnt[u]++; belong[u]=u; inS[u]=false; s.pop(); } } void getFlag(int u){ if(vis[u]==true) return; for(int v:ee[u]){ getFlag(v); flag[u] = flag[u]|flag[v]; flag[u].set(v); } vis[u]=true; } int main(){ ios::sync_with_stdio(false); cin.tie(0); //读取原图 cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>g[i][j]; for(int u=1;u<=n;u++){ for(int v=1;v<=n;v++){ if(g[u][v]=='1') e[u].push_back(v); } } //原图的强连通分量缩点 for(int i=1;i<=n;i++) if(dfn[i]==0) dfs(i); /* for(int i=1;i<=n;i++) cout<<belong[i]<<" "<<cnt[i]<<endl; */ //构建新图(正图) for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) if(g[i][j]=='1') if(belong[i]!=belong[j]){ ee[belong[i]].push_back(belong[j]); //cout<<belong[i]<<"->"<<belong[j]<<endl; } if(belong[i]==i) points.push_back(i); } //新图是一个dag,拓扑排序的过程中求解新图中的点连接关系 for(int u:points) if(vis[u]==false) getFlag(u); //统计答案 ans=0; for(int u:points){ ans+=cnt[u]*cnt[u]; for(int v:points){ if(flag[u][v]==true) ans+=cnt[u]*cnt[v]; } } cout<<ans<<"\n"; return 0; }
-
0
P3387 【模板】缩点
#include <bits/stdc++.h> using namespace std; const int MAXN = 10000; const int MAXM = 100000; //原图 int n, m; int a[MAXN + 5]; //点权 vector<int> e[MAXN + 5]; //存图 //tarjan int idx, dfn[MAXN + 5], low[MAXN + 5]; stack<int> s; bool inS[MAXN + 5]; //每个点是否在栈中 //存SCC int sccCnt; vector<int> scc[MAXN + 5]; //新图(复用 a[] 存新图的每个点权值) int id[MAXN + 5]; //缩到了新图的哪个点上 vector<int> ee[MAXN + 5]; //新图存边 void dfs(int u) { dfn[u] = low[u] = ++idx; s.push(u); inS[u] = true; for (int v : e[u]) { if(dfn[v] == 0) { dfs(v); low[u] = min(low[u], low[v]); } else if(inS[v]) { low[u] = min(low[u], dfn[v]); } } //当前点是某个 scc 的根 if(dfn[u] == low[u]) { ++sccCnt; while(s.top() != u) { id[s.top()] = u, a[u] += a[s.top()]; // 缩点 inS[s.top()] = false; scc[sccCnt].push_back(s.top()); s.pop(); } //处理 u id[s.top()] = u; // 缩点 inS[s.top()] = false; scc[sccCnt].push_back(s.top()); s.pop(); } } //拓扑排序、DAG 上 DP int f[MAXN + 5]; //走到每个点的最大权值之和 int d[MAXN + 5]; //新图的每个点入度 queue<int> q; int main() { ios::sync_with_stdio(false); cin.tie(0); //输入 cin >> n >> m; for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= m; i++) { int u, v; cin >> u >> v; e[u].push_back(v); } //缩点 for(int i = 1; i <= n; i++) if(dfn[i] == 0) dfs(i); /* cout << sccCnt << "\n"; for(int i = 1; i <= sccCnt; i++) { cout << scc[i].size() << ":"; for(int x : scc[i]) cout << x << " "; cout << "\n"; } for(int i=1;i<=n;i++) cout << id[i] << ",\n"[i==n]; */ //建新图 for(int u = 1; u <= n; u++) for(int v : e[u]) if(id[u] != id[v]) { ee[id[u]].push_back(id[v]); d[id[v]]++; // cout << id[u] << "~" << id[v] << "\n"; } for(int i = 1; i <= n; i++) if(d[i] == 0) { q.push(i); f[i] = a[i]; } int ans = -1; while(!q.empty()) { int u = q.front(); q.pop(); ans = max(ans, f[u]); for(int v : ee[u]){ f[v] = max(f[v], f[u] + a[v]); d[v]--; if(d[v] == 0) q.push(v); } } cout << ans << "\n"; return 0; } /* 10 20 986 587 671 143 521 985 204 36 921 990 4 2 2 8 7 8 2 4 1 7 8 7 10 2 5 9 4 5 5 8 4 9 2 7 5 6 9 1 9 6 4 10 9 7 9 2 1 2 5 7 ==== 5133 */
-
0
P8435 【模板】点双连通分量
#include <bits/stdc++.h> using namespace std; const int MAXN = 500000; const int MAXM = 2000000; int n, m; //<终点坐标,边的编号> vector<pair<int, int>> edge[MAXN + 5]; // edge[i]:与i相连的所有边 bool visE[MAXM + 5]; // 每条边是否做过了 bool vis[MAXN + 5]; // vis[i]: i有没有走过 int low[MAXN + 5]; // low[i]:不经过父节点能走到的最小时间戳; int dfn[MAXN]; // dfn[i]:深搜生成树的时间戳 int idx; // idx:时间戳 stack<int> tempS; int v_bcc_cnt; // 点双数量 vector<int> v_bcc[MAXN + 5]; // 存所有点双 void dfs(int u, int eId) { tempS.push(u); vis[u] = visE[eId] = true; low[u] = dfn[u] = ++idx; int childCnt = 0; for (int i = 0; i < edge[u].size(); i++) { int v = edge[u][i].first; // u->v; int uvId = edge[u][i].second; if (!vis[v]) { childCnt++; dfs(v, uvId); low[u] = min(low[u], low[v]); // 根节点可以一起做,不管是不是割点,根节点都可以加入下面的点双 if (low[v] >= dfn[u]) { v_bcc_cnt++; while (tempS.top() != v) { v_bcc[v_bcc_cnt].push_back(tempS.top()); tempS.pop(); } v_bcc[v_bcc_cnt].push_back(tempS.top()); tempS.pop(); v_bcc[v_bcc_cnt].push_back(u); } } else if (!visE[uvId]) { low[u] = min(low[u], dfn[v]); } } if (eId == 0 && childCnt == 0) v_bcc[++v_bcc_cnt].push_back(u); } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; edge[u].push_back(make_pair(v, i)); edge[v].push_back(make_pair(u, i)); } for (int i = 1; i <= n; i++) { if (!vis[i]) { idx = 0; dfs(i, 0); } } cout << v_bcc_cnt << endl; for (int i = 1; i <= v_bcc_cnt; i++) { cout << v_bcc[i].size() << " "; for (auto x : v_bcc[i]) cout << x << " "; cout << endl; } return 0; }
-
0
P8436 【模板】边双连通分量
#include <bits/stdc++.h> using namespace std; const int MAXN = 500000; const int MAXM = 2000000; int n, m; //<终点坐标,边的编号> vector<pair<int, int>> edge[MAXN + 5]; // edge[i]:与i相连的边 bool visE[MAXM + 5]; // 每条边是否做过了 bool vis[MAXN + 5]; // vis[i]: i有没有走过; bool flag[MAXN + 5]; // flag[i]:fa[i]~i 是否为桥 int low[MAXN + 5]; // low[i]:不经过父节点能走到的最小时间戳; int dfn[MAXN]; // dfn[i]:深搜生成树的时间戳 int idx, resCnt; // idx:时间戳; resCnt:割点数量 // 并查集 int fa[MAXN + 5]; int findFa(int x) { if (x == fa[x]) return x; return fa[x] = findFa(fa[x]); } void merge(int x, int y) { cout << endl; int faX = findFa(x); int faY = findFa(y); if (faX != faY) fa[faX] = faY; } // 存所有边双 vector<int> e_bcc[MAXN + 5]; void dfs(int u, int eId) { vis[u] = true; visE[eId] = true; low[u] = dfn[u] = ++idx; for (int i = 0; i < edge[u].size(); i++) { int v = edge[u][i].first; // u->v; int uvId = edge[u][i].second; // u->v; if (!vis[v]) { dfs(v, uvId); low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) { // u->v 是桥 // cout << u << "x" << v << endl; } else { merge(u, v); // cout << u << "~" << v << endl; } } else if (!visE[uvId]) { low[u] = min(low[u], dfn[v]); } } } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; edge[u].push_back(make_pair(v, i)); edge[v].push_back(make_pair(u, i)); } for (int i = 1; i <= n; i++) fa[i] = i; for (int i = 1; i <= n; i++) { if (!vis[i]) { idx = 0; dfs(i, 0); } } int cnt = 0; for (int i = 1; i <= n; i++) { int faI = findFa(i); if (e_bcc[faI].empty()) cnt++; e_bcc[faI].push_back(i); } cout << cnt << "\n"; for (int i = 1; i <= n; i++) { if (!e_bcc[i].empty()) { cout << e_bcc[i].size() << " "; for (int x : e_bcc[i]) cout << x << " "; cout << "\n"; } } return 0; }
-
0
P1656 炸铁路
#include <bits/stdc++.h> using namespace std; const int MAXN = 150; int n, m; vector<int> edge[MAXN + 5]; // edge[i]:与i相连的点 bool vis[MAXN + 5]; // vis[i]: i有没有走过; int fa[MAXN + 5]; // fa[i]:i 的父节点 bool flag[MAXN + 5]; // flag[i]:fa[i]~i 是否为桥 int low[MAXN + 5]; // low[i]:不经过父节点能走到的最小时间戳; int dfn[MAXN]; // dfn[i]:深搜生成树的时间戳 int idx, resCnt; // idx:时间戳; resCnt:割点数量 vector<pair<int, int>> ans; void dfs(int u, int father) { vis[u] = true; low[u] = dfn[u] = ++idx; fa[u] = father; for (int i = 0; i < edge[u].size(); i++) { int v = edge[u][i]; // u->v; if (!vis[v]) { dfs(v, u); low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) { ans.push_back(make_pair(min(u, v), max(u, v))); flag[v] = true; } } else if (v != father) { low[u] = min(low[u], dfn[v]); } } } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; edge[u].push_back(v); edge[v].push_back(u); } for (int i = 1; i <= n; i++) { if (!vis[i]) { idx = 0; dfs(i, i); } } sort(ans.begin(), ans.end()); for (auto x : ans) cout << x.first << " " << x.second << "\n"; return 0; }
-
0
P3388 【模板】割点(割顶)
#include <bits/stdc++.h> using namespace std; const int MAXN = 100000; int n, m; vector<int> edge[MAXN + 5]; // edge[i]:与i相连的点 bool vis[MAXN + 5], flag[MAXN + 5]; // vis[i]: i有没有走过;flag[i]:i是否为割点 int low[MAXN + 5]; // low[i]:不经过父节点能走到的最小时间戳; int dfn[MAXN]; // dfn[i]:深搜生成树的时间戳 int idx, resCnt; // idx:时间戳; resCnt:割点数量 void dfs(int u, int fa) { vis[u] = true; low[u] = dfn[u] = ++idx; int childCnt = 0; for (int i = 0; i < edge[u].size(); i++) { int v = edge[u][i]; // u->v; if (!vis[v]) { childCnt++; dfs(v, u); low[u] = min(low[u], low[v]); if (u != fa && low[v] >= dfn[u] && !flag[u]) { resCnt++; flag[u] = true; } } else { low[u] = min(low[u], dfn[v]); } } if (u == fa && childCnt >= 2 && !flag[u]) { resCnt++; flag[u] = true; } } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; edge[u].push_back(v); edge[v].push_back(u); } for (int i = 1; i <= n; i++) { if (!vis[i]) { idx = 0; dfs(i, i); } } cout << resCnt << endl; for (int i = 1; i <= n; i++) if (flag[i]) cout << i << " "; return 0; }
-
0
P3443 [POI2006]LIS-The Postman
代码写得很乱,以后有空修缮一下
#include <bits/stdc++.h> using namespace std; // 存图 const int MAXN = 400000; const int MAXM = 800000; int n, m, t; // 初始的边 int uu[MAXM + 5], vv[MAXM + 5]; // 初始的边 ee[起点][终点] = 边的编号 map<int, int> ee[MAXN + 5]; // 必要路径的边的前驱后继 int pre[MAXM + 5], nxt[MAXM + 5]; // 当前边作为必要路径链的起点时的对应终点 int fnl[MAXM + 5]; // 存所有的压缩边:<压缩后的边,压缩路径的第一条的编号> multimap<pair<int, int>, int> spe; // 当前必要路径的每条边的ID int nowPath[MAXM + 5]; bool pathFlag[MAXM + 5]; // 欧拉路相关 // pair 为 <边的终点,边的编号> vector<pair<int, int>> e[MAXN + 5]; // 每个点的入度/出度 int inD[MAXN + 5], outD[MAXN + 5]; // 每个点已经搞定了几条边 int cnt[MAXN + 5]; // 最终路径 stack<int> ans; vector<int> path; void dfs(int u) { while (cnt[u] < e[u].size()) { int v = e[u][cnt[u]].first; int eId = e[u][cnt[u]].second; cnt[u]++; dfs(v); } ans.push(u); } // 判断图是否连通 int fa[MAXN + 5]; set<int> points; // 新图的点的编号 int findFa(int x) { if (x == fa[x]) return x; return fa[x] = findFa(fa[x]); } void merge(int x, int y) { int faX = findFa(x); int faY = findFa(y); if (faX != faY) fa[faX] = faY; } int main() { ios::sync_with_stdio(false); cin.tie(0); // 输入 cin >> n >> m; for (int i = 1; i <= m; i++) { cin >> uu[i] >> vv[i]; ee[uu[i]][vv[i]] = i; outD[uu[i]]++; inD[vv[i]]++; } // 判断初始图是否有回路 for (int i = 1; i <= n; i++) if (inD[i] != outD[i]) { cout << "NIE\n"; return 0; } cin >> t; while (t--) { int len, last, now, eId; cin >> len; // 合并要求的路径,构建每条边的前驱后继 cin >> last; for (int i = 2; i <= len; i++) { cin >> now; eId = ee[last][now]; nowPath[i] = eId; // last->now 检查是否存在这条边 if (eId == 0) { cout << "NIE\n"; return 0; } // nowPath[i-1] -> eId; // 检查边是否有多个前驱后继 if (i > 2 && (pre[eId] != 0 && pre[eId] != nowPath[i - 1] || nxt[nowPath[i - 1]] != 0 && nxt[nowPath[i - 1]] != eId)) { cout << "NIE\n"; return 0; } // 连接前驱后继 if (i > 2) { pre[nowPath[i]] = nowPath[i - 1]; nxt[nowPath[i - 1]] = nowPath[i]; } last = now; } // 检查当前路径是否有重边 for (int i = 2; i <= len; i++) pathFlag[nowPath[i]] = false; for (int i = 2; i <= len; i++) if (pathFlag[nowPath[i]] == true) { cout << "NIE\n"; return 0; } else { pathFlag[nowPath[i]] = true; } } // 构建要求的路径 for (int i = 1; i <= m; i++) { int eId = i; if (pre[eId] == 0 && nxt[eId] != 0) { while (nxt[eId] != 0) eId = nxt[eId]; fnl[i] = vv[eId]; spe.insert(make_pair(make_pair(uu[i], fnl[i]), i)); } } // 构建新图 for (int i = 1; i <= n; i++) fa[i] = i; for (int i = 1; i <= m; i++) { if (pre[i] != 0) continue; int u = uu[i], v = vv[i]; if (nxt[i] != 0) v = fnl[i]; e[u].push_back(make_pair(v, i)); points.insert(u); points.insert(v); merge(u, v); // 计算入度出度 outD[u]++; inD[v]++; } // 检查新图能不能从1号点开始走 if (e[1].empty()) { cout << "NIE\n"; return 0; } // 检查新图有没有欧拉回路 for (int i = 1; i <= n; i++) { if (inD[i] != outD[i]) { cout << "NIE\n"; return 0; } } // 检查新图是否连通 for (auto i : points) { if (findFa(i) != findFa(*(points.begin()))) { cout << "NIE\n"; return 0; } } dfs(1); // 解压 path.push_back(ans.top()); int last = ans.top(); ans.pop(); while (!ans.empty()) { int now = ans.top(); ans.pop(); auto it = spe.find(make_pair(last, now)); if (it == spe.end()) path.push_back(now); else { int eId = it->second; path.push_back(vv[eId]); while (nxt[eId] != 0) { eId = nxt[eId]; path.push_back(vv[eId]); } spe.erase(it); } last = now; } // 检查并输出 if (path.size() == m + 1) { cout << "TAK\n"; for (auto x : path) cout << x << "\n"; } else { cout << "NIE\n"; } return 0; }
-
0
P2731 [USACO3.3]骑马修栅栏 Riding the Fences
#include <bits/stdc++.h> using namespace std; // 存图 const int MAXN = 500; const int MAXM = 2048; int n, m; // pair 为 <边的终点,边的编号> vector<pair<int, int>> e[MAXN + 5]; // 每个点的度 int d[MAXN + 5]; // 欧拉路的起点终点 int st, ed; // 每条边还在不在 bool vis[MAXM + 5]; // 最终路径 stack<int> ans; void dfs(int u) { for (int i = 0; i < e[u].size(); i++) { int v = e[u][i].first; int eId = e[u][i].second; if (vis[eId]) continue; if (eId <= m) { vis[eId] = true; vis[eId + m] = true; } else { vis[eId] = true; vis[eId - m] = true; } dfs(v); } ans.push(u); } bool pFlag[MAXN + 5]; vector<int> points; int main() { ios::sync_with_stdio(false); cin.tie(0); // 输入 cin >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; pFlag[u] = pFlag[v] = true; e[u].push_back(make_pair(v, i)); e[v].push_back(make_pair(u, i + m)); // 计算入度出度 d[u]++; d[v]++; } for (int i = 1; i <= MAXN; i++) if (pFlag[i]) points.push_back(i); // 如果题目未保证弱连通,那么需要先判断一下 // 找欧拉路的起点终点 st = ed = 0; for (int i : points) { if (d[i] % 2 != 0) { if (st == 0) st = i; else if (ed == 0) ed = i; else { cout << "No\n"; return 0; } } } if (st != 0 && ed == 0) { cout << "No\n"; return 0; } if (st == 0 && ed == 0) st = ed = points[0]; if (st > ed) swap(st, ed); // 题目要求按字典序最小的欧拉路,排个序 for (int i : points) sort(e[i].begin(), e[i].end()); dfs(st); while (!ans.empty()) { cout << ans.top() << "\n"; ans.pop(); } cout << "\n"; return 0; }
-
0
P7771 【模板】欧拉路径
#include <bits/stdc++.h> using namespace std; // 存图 const int MAXN = 100000; const int MAXM = 200000; int n, m; // 存图 vector<int> e[MAXN + 5]; // 每个点的入度/出度 int inD[MAXN + 5], outD[MAXN + 5]; // 欧拉路的起点终点 int st, ed; // 每个点已经搞定了几条边 int cnt[MAXN + 5]; // 每条边还在不在 bool vis[MAXM + 5]; // 最终路径 stack<int> ans; void dfs(int u) { while (cnt[u] < e[u].size()) { int v = e[u][cnt[u]]; cnt[u]++; dfs(v); } ans.push(u); } int main() { ios::sync_with_stdio(false); cin.tie(0); // 输入 cin >> n >> m; for (int i = 1; i <= m; i++) { int u, v; cin >> u >> v; e[u].push_back(v); // 计算入度出度 outD[u]++; inD[v]++; } // 如果题目未保证弱连通,那么需要先判断一下 // 找欧拉路的起点终点 int cntD = 0; // 出度入度不相等的点的数量 bool flag = false; // 是否存在出入度相差大于1的点 st = ed = 0; for (int i = 1; i <= n; i++) { if (inD[i] != outD[i]) { cntD++; if (abs(inD[i] - outD[i]) > 1) flag = true; if (inD[i] + 1 == outD[i]) st = i; if (outD[i] + 1 == inD[i]) ed = i; } } // 判断是否存在欧拉路 if ((cntD != 2 && cntD != 0) || flag || (cntD == 2 && (!st || !ed))) { cout << "No\n"; return 0; } if (cntD == 0) st = ed = 1; // 题目要求按字典序最小的欧拉路,排个序 for (int i = 1; i <= n; i++) sort(e[i].begin(), e[i].end()); dfs(st); while (!ans.empty()) { cout << ans.top() << " "; ans.pop(); } cout << "\n"; return 0; }
- 1
信息
- ID
- 1285
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 12
- 已通过
- 11
- 上传者