题解(图论+codeforce)

洛谷P3387 【模板】缩点
思路:tarjan求出强连通分量,缩点建新图,之后拓扑序dp
 

#include
#include
#include
#include
#include
#define int long long
const int MAXN = 1e5+10;
using namespace std;
vector edges[MAXN];
vector edges2[MAXN];
stackstk;
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];         //记录值包括(强连通分量)
    }
    queueq;
    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
#include
#include
#include
const int MAXN = 5e4+10;
using namespace std;
vector edges[MAXN];
vector edges2[MAXN];
stackstk;
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
#include
#include
#include
using namespace std;
const int MAXN = 5e3+10;
vector bridges;
int dfsn[MAXN], low[MAXN], fa[MAXN], cnt;
vectoredges[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  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
#include
#include
#include
using namespace std;
const int MAXN = 1000000;
int dfsn[MAXN], low[MAXN];
vectoredges[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
#include
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
#include
#include
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;
}

你可能感兴趣的:(c++,算法,图论)