洛谷P3387 【模板】缩点
思路:tarjan求出强连通分量,缩点建新图,之后拓扑序dp
#include<iostream>
#include<stack>
#include<algorithm>
#include<vector>
#include<queue>
#define int long long
const int MAXN = 1e5+10;
using namespace std;
vector<int> edges[MAXN];
vector<int> edges2[MAXN];
stack<int>stk;
int dfsn[MAXN], low[MAXN], instk[MAXN], scc[MAXN], cnt, cscc;
int ans;
int a[MAXN],w[MAXN],dis[MAXN];
int d[MAXN];
void tarjan(int p)
{
low[p] = dfsn[p] = ++cnt;
instk[p] = 1;
stk.push(p); // 进栈
for (auto q : edges[p])
{
if (!dfsn[q]) // 未访问过
{
tarjan(q); // 递归地搜索
low[p] = min(low[p], low[q]);
}
else if (instk[q]) // 访问过,且q可达p
low[p] = min(low[p], dfsn[q]);
}
if (low[p] == dfsn[p]) // 发现强连通分量的根
{
int top;
cscc++;
do
{
top = stk.top();
stk.pop();
instk[top] = 0;
scc[top] = cscc; // 记录所属的强连通分量
} while (top != p); // 直到弹出p才停止
}
}
signed main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
edges[a].push_back(b);
}
for (int i = 1; i <= n; ++i)
if (!dfsn[i])
tarjan(i);
for (int u = 1; u <= n; ++u) { //缩点
for (auto v : edges[u]) {
if (scc[u] != scc[v]) {
edges2[scc[u]].push_back(scc[v]);
d[scc[v]]++;
}
}
w[scc[u]] += a[u]; //记录值包括(强连通分量)
}
queue<int>q;
for (int i = 1; i <= cscc; i++) {
if (d[i] == 0) { //选择入度为0的点
dis[i] = w[i];
q.push(i);
}
}
while (q.size()) { //直到入度为0的点不存在
int u = q.front();
q.pop();
for (auto v : edges2[u]) {
dis[v] = max(dis[v], dis[u] + w[v]);
d[v]--; //删除与之相连的所有边,即入度-1
if (d[v] == 0) {
q.push(v);
}
}
}
for (int i = 1; i <= cnt; i++) { //比较以i为终点的最大值
ans = max(ans, dis[i]);
}
cout << ans << '\n';
return 0;
}
P2863[USACO06JAN]The Cow Prom S
tarjan算法求出强连通分量,统计强连通分量>1的即可
#include<iostream>
#include<stack>
#include<algorithm>
#include<vector>
const int MAXN = 5e4+10;
using namespace std;
vector<int> edges[MAXN];
vector<int> edges2[MAXN];
stack<int>stk;
int dfsn[MAXN], low[MAXN], instk[MAXN], scc[MAXN], cnt, cscc;
int ans;
bool vis[MAXN];
int Cnt[MAXN];
void tarjan(int p)
{
low[p] = dfsn[p] = ++cnt;
instk[p] = 1;
stk.push(p); // 进栈
for (auto q : edges[p])
{
if (!dfsn[q]) // 未访问过
{
tarjan(q); // 递归地搜索
low[p] = min(low[p], low[q]);
}
else if (instk[q]) // 访问过,且q可达p
low[p] = min(low[p], dfsn[q]);
}
if (low[p] == dfsn[p]) // 发现强连通分量的根
{
int top;
cscc++;
do
{
top = stk.top();
stk.pop();
instk[top] = 0;
scc[top] = cscc; // 记录所属的强连通分量
} while (top != p); // 直到弹出p才停止
}
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int a, b;
cin >> a >> b;
edges[a].push_back(b);
}
for (int i = 1; i <= n; ++i)
if (!dfsn[i])
tarjan(i);
for (int i = 1; i <= n; i++) {
Cnt[scc[i]]++;
if (Cnt[scc[i]] > 1 && !vis[scc[i]]) {
ans++;
vis[scc[i]] = 1;
}
}
cout << ans << '\n';
return 0;
}
P1656 炸铁路
割桥模板题
如果 p 是 q 的父节点,并且low(q)>dfsn(p) ,那么 p<->q 是桥。
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 5e3+10;
vector<int> bridges;
int dfsn[MAXN], low[MAXN], fa[MAXN], cnt;
vector<int>edges[MAXN];
int sum = 0,ans;
struct edge {
int u, v;
}e[MAXN];
bool cmp(edge x, edge y) {
if (x.u == y.u)
return x.v < y.v;
return x.u <y.u;
}
void tarjan(int p)
{
low[p] = dfsn[p] = ++cnt;
for (auto to : edges[p])
{
if (!dfsn[to])
{
fa[to] = p; // 记录父节点
tarjan(to);
low[p] = min(low[p], low[to]);
if (low[to] > dfsn[p]) {
e[ans].u = min(to,p);
e[ans].v = max(to, p);
ans++;
}
}
else if (fa[p] != to) // 排除父节点
low[p] = min(low[p], dfsn[to]);
}
}
int main()
{
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
edges[a].push_back(b);
edges[b].push_back(a);
}
for (int i = 1; i <= n; ++i)
if (!dfsn[i])
tarjan(i);
sort(e, e + ans, cmp);
for (int i = 0; i < ans; i++) {
cout << e[i].u << ' ' << e[i].v << '\n';
}
return 0;
}
洛谷P3388 【模板】割点(割顶)
给出一个 n 个点,m 条边的无向图,求图的割点。
如果 p 存在一个子结点 q 满足 low(q)≥dfsn(p) ,则p为割点
特殊情况,根节点需要有两个子节点才为割点
#include<iostream>
#include<vector>
#include<stack>
#include<cstring>
using namespace std;
const int MAXN = 1000000;
int dfsn[MAXN], low[MAXN];
vector<int>edges[MAXN];
bool cut[MAXN];
int sum = 0 , cnt;
void tarjan(int p, bool root = true)
{
int tot = 0;
low[p] = dfsn[p] = ++cnt;
for (auto q : edges[p])
{
if (!dfsn[q])
{
tarjan(q, false);
low[p] = min(low[p], low[q]);
tot += (low[q] >= dfsn[p]); // 统计满足low[q] >= dfsn[p]的子节点数目
}
else
low[p] = min(low[p], dfsn[q]);
}
if (tot > root) { // 如果是根,tot需要大于1;否则只需大于0
sum++;
cut[p] = true;
}
}
int main()
{
ios::sync_with_stdio(false);
int n, m;
cin >> n >> m;
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
edges[a].push_back(b);
edges[b].push_back(a);
}
for (int i = 1; i <= n; ++i)
if (!dfsn[i])
tarjan(i);
cout << sum << '\n';
for (int i = 1; i <= n; i++) {
if (cut[i])
cout << i << ' ';
}
return 0;
}
Codeforces Round 863 (Div. 3)
E
思路:缺少一个数字,转换成9进制,缺少4,遇见大于4的+1即可
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
signed main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--) {
int k;
cin >> k;
string s;
int flag = 0;
while (k) {
s += k % 9 + '0';
k /= 9;
flag++;
}
for (int i = 0; i < s.size(); i++) {
if (s[i] >= '4')
s[i]++;
}
reverse(s.begin(), s.end());
cout << s << '\n';
}
return 0;
}
B.Conveyor Belts
思路:传送阵的范围为(x,x到n+1-x或n+1-x到x),(y到n+1-y或n+1-y到y,y)
从起始位置到目标位置的最小能量,确定两者的最小可达,相减的绝对值即可
#include<iostream>
#include<math.h>
#include<algorithm>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
int t;
cin >> t;
while (t--) {
int n, x1, y1, x2, y2;
cin >> n >> x1 >> y1 >> x2 >> y2;
int x = min(min(x1, n - x1 + 1),min(y1, n - y1 + 1));
int y = min(min(x2, n - x2 + 1), min(y2, n - y2 + 1));
cout << abs(x - y) << '\n';
}
return 0;
}