联合训练图论场

联合训练图论场题解报告

传送门


A.Euler

题意:略

分析:

这题主要是先掌握欧拉通路的概念,然后是如何判断图是否存在欧拉通路。
欧拉通路:通过图中每条边且只通过一次,并且经过每一顶点的通路。
欧拉回路:通过图中每条边且只通过一次,并且经过每一顶点的回路。
无向图:
    欧拉通路:连通图+只存在0个或者两个度数为奇数的点。
    欧拉回路:连通图+所有节点的度数均为偶数。
有向图:
    欧拉通路:连通图+(所有点的入度=出度 || 出两个点之外其他点的入度=出度,一个点的入度-出度=1,一个点的出度-入度=1)。
ii e[500*500];
int in[510], out[510];//indegree,outdegree
int f[510];//判断是否连通
int find(int x) {
    return f[x] == -1 ? x : f[x] = find(f[x]);
}
int main(int argc, const char * argv[])
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    int t, n, m;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for (int i = 0;i < m;++i)
            scanf("%d%d", &e[i].first, &e[i].second);
        memset(in, 0,sizeof in);
        memset(out, 0,sizeof out);
        memset(f, -1,sizeof f);
        int cnt = 0;
        for (int i = 0;i < m;++i) {
            in[e[i].first]++;
            in[e[i].second]++;
            int t1 = find(e[i].first);
            int t2 = find(e[i].second);
            if (t1 != t2) f[t1] = t2;
        }
        int o=0;
        for (int i = 1;i <= n;++i) {
            find(i);
            if (f[i] == -1) o++;
        }//o == 说明图连通
        for (int i = 1;i <= n;++i)
            if (in[i] & 1) cnt++;
        if (cnt == 0 || cnt == 2) {
            if (o == 1) printf("Yes");
            else printf("No");
        }else printf("No");
        printf(" ");

        memset(in, 0,sizeof in);
        memset(f, -1,sizeof f);
        cnt = 0;
        for (int i = 0;i < m;++i) {
            out[e[i].first]++;
            in[e[i].second]++;
        }
        if (o > 1) puts("No");
        else {
            vector<int> vec;
            for (int i = 1;i <= n;++i) {
                if (in[i] != out[i]) vec.push_back(i);
            }
            if (vec.size() != 2 && vec.size() != 0) puts("No");
            else {
                if (vec.size() == 0) {
                    puts("Yes");
                    continue;
                }
                int u = vec[0], v = vec[1];
                if (in[u] - out[u] == 1 && in[v] - out[v] == -1) puts("Yes");
                else if (in[u] - out[u] == -1 && in[v] - out[v] == 1) puts("Yes");
                else puts("No");
            }
        }
    }
    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}

B.-0你的电脑炸了

题意

判断给出的图是否合法。

分析

4*4的格子中,每个位置会出现指定的某些数字,但是由于覆盖的作用,只会看见最上面的一个,其他的被压在了下面。A覆盖了B,B覆盖了A,这样是显然不成立的,这题就是判断是否会出现相互覆盖的情况,拓普排序。
ps:初始化的表要仔细打。。。
vector<int> have[5][5];
int a[5][5];
int in[10];
vector<int> G[10];
void init() {
    have[1][1].push_back(1);
    have[1][2].push_back(1), have[1][2].push_back(2);
    have[1][3].push_back(2), have[1][3].push_back(3);
    have[1][4].push_back(3);

    have[2][1].push_back(1), have[2][1].push_back(4);
    have[2][2].push_back(1), have[2][2].push_back(2), have[2][2].push_back(4), have[2][2].push_back(5);
    have[2][3].push_back(2), have[2][3].push_back(3), have[2][3].push_back(5), have[2][3].push_back(6);
    have[2][4].push_back(3), have[2][4].push_back(6);

    have[3][1].push_back(4), have[3][1].push_back(7);
    have[3][2].push_back(4), have[3][2].push_back(5), have[3][2].push_back(7), have[3][2].push_back(8);
    have[3][3].push_back(5), have[3][3].push_back(6), have[3][3].push_back(8), have[3][3].push_back(9);
    have[3][4].push_back(6), have[3][4].push_back(9);

    have[4][1].push_back(7);
    have[4][2].push_back(7), have[4][2].push_back(8);
    have[4][3].push_back(8), have[4][3].push_back(9);
    have[4][4].push_back(9);
}
void done(int u,int i, int j) {
    for (int k = 0;k < have[i][j].size();++k) {
        int v = have[i][j][k];
        if (u == v) continue;
        G[u].push_back(v);
        in[v]++;
    }
}
void getmap() {
    for (int i = 1;i <= 9;++i) {
        G[i].clear();
        in[i] = 0;
    }
    for (int i = 1;i <= 4;++i) {
        for (int j = 1;j <= 4;++j)
            done(a[i][j], i, j);
    }
}
void solve() {
    getmap();
    queue<int> que;
    for (int i = 1;i <= 9;++i) {
        if (in[i] == 0) {
            que.push(i);
        }
    }
    while(!que.empty()) {
        int u = que.front();
        que.pop();
        for (int i = 0;i < G[u].size();++i) {
            int v = G[u][i];
            if (--in[v] == 0) {
                que.push(v);
            }
        }
    }
    bool ok = true;
    for (int i = 1;i <= 9;++i)
        if (in[i] > 0) ok = false;
    if (ok) puts("Lucky dog!");
    else puts("BOOM!");
}
int main(int argc, const char * argv[])
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    init();
    int T;
    cin >> T;
    while(T--) {
        for (int i = 1;i <= 4;++i) {
            for (int j = 1;j <= 4;++j)
                scanf("%d", &a[i][j]);
        }
        solve();
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}

C.寻找fly真迹

题意

分析

首先用补图还是很容易想到的(毕竟正着搞相对复杂了)。建立补图之后很容易就想到了二分图染色,最后判断是否成立。
const int maxn = 5e2 + 10;
int n, m;
int col[maxn];
vector<int> G[maxn];
int g[maxn][maxn];
bool dfs(int u,int color) {
    col[u] = color;
    for (int i = 0;i < G[u].size();++i) {
        int v = G[u][i];
        if (col[u] == col[v]) return false;
        if (!col[v] && !dfs(v, -color)) return false;
    }
    return true;
}
int main(int argc, const char * argv[])
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    int t;
    cin >> t;
    while(t--) {
        scanf("%d%d", &n, &m);
        memset(g, 0,sizeof g);
        int u, v;
        for (int i = 1;i <= m;++i) {
            scanf("%d%d", &u, &v);
            g[u][v] = g[v][u] = 1;
        }
        for (int i = 1;i <= n;++i) {
            G[i].clear();
            for (int j = 1;j <= n;++j)
                if (i != j && !g[i][j]) G[i].push_back(j);
        }
        bool yes = true;
        memset(col, 0,sizeof col);
        for (int i = 1;i <= n;++i) {
            if (!col[i] && G[i].size() && !dfs(i, 1)) {
                yes=false;
            }
        }
        for (int i = 1;i <= n;++i) {
            for (int j = 1;j <= n;++j) {
                if (i == j) continue;
                if (g[i][j] && col[i]*col[j]<0)yes=false;
                if (!g[i][j] && col[i]*col[j]>=0)yes=false;
            }
        }
        if (yes) puts("Yes");
        else puts("No");
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}

D.一食堂or二食堂,it’s a question

题意

好懂,可能就“使得任意两人走过的距离加上二人所在食堂的距离的最大值最小”这句话需要解释下。任意两个人A, B 如果在同一个食堂的话就是他两个走过的距离和,如果不在同一个食堂就再加上两个食堂之间的距离。

分析

求最大最小,显然二分结果值。然后根据二分值建图判断可行性。
我们可以用bool值表示每个人的选择。对于第i个人而言,i表示其选择第一食堂,i+N表示其选择二食堂。
<1> 相互憎恨的两个人x,y
必然建立四条边(x,y+N), (y,x+N),(x+N,y),(y+N,x)。表示两个选择不同的食堂。
<2> 相互喜欢的两个人x,y
必然建立四条边(x,y),(y,x),(x+N,y+N),(y+N,x+N)表示两个人选择同一个食堂。
<3> 对于人意的两个人x,y
这里就看代码了。。。
/*****************************************
Author      :Crazy_AC(JamesQi)
Time        :2016
File Name   :
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---‘\____
             .‘  \|     |//  `.
            /  \|||  :  |||//             /  _||||| -:- |||||-             |   | \\  -  /// |   |
           | \_|  ‘‘\---/‘‘  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .‘  /--.--\  `. . __
      ."" ‘<  `.___\_<|>_/___.‘  >‘"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-‘======
                   `=---=‘
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
*****************************************/
// #pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <climits>
using namespace std;
#define MEM(x,y) memset(x, y,sizeof x)
#define pk push_back
#define lson rt << 1
#define rson rt << 1 | 1
#define bug cout << "BUG HERE\n"
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> ii;
typedef pair<ii,int> iii;
const double eps = 1e-8;
const double pi = 4 * atan(1);
const int inf = 1 << 30;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int nCase = 0;
int dcmp(double x){//精度正负、0的判断
    if (fabs(x) < eps) return 0;
    return x < 0?-1:1;
}
inline int read(){
    char c = getchar();
    while (!isdigit(c)) c = getchar();
    int x = 0;
    while (isdigit(c)) {
        x = x * 10 + c - ‘0‘;
        c = getchar();
    }
    return x;
}
const int maxn = 2010;
struct Edge{
    int to, nxt;
    Edge() {}
    Edge(int to,int nxt) {
        this->to = to;
        this->nxt = nxt;
    }
}edges[maxn*maxn];
int N, A, B;
int head[maxn], ecnt;
void add(int u,int v) {
    edges[ecnt] = Edge(v, head[u]), head[u] = ecnt++;
}
int dfn[maxn], low[maxn], depth;
bool in[maxn];
stack<int> st;
int belong[maxn];
int block;
void tarjan(int u) {
    dfn[u] = low[u] = ++depth;
    in[u] = true;
    st.push(u);

    for (int i = head[u]; ~i;i = edges[i].nxt) {
        int v = edges[i].to;
        if (dfn[v] == -1) {
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }else if (in[v]) low[u] = min(low[u], dfn[v]);
    }

    if (dfn[u] == low[u]) {
        block++;
        while(true) {
            int x = st.top();
            st.pop();
            in[x] = false;
            belong[x] = block;
            if (x == u) break;
        }
    }
}
struct point {
    int x, y;
    void read() {
        scanf("%d%d", &x, &y);
    }
}p[maxn], hate[maxn], like[maxn], s1, s2;
int dis[maxn][maxn];
inline int dist(point& a,point& b) {//曼哈顿距离
    return abs(a.x - b.x) + abs(a.y - b.y);
}
bool ok() {
    depth = block = 0;
    memset(dfn, -1,sizeof dfn);
    for (int i = 1;i <= 2*N;++i)
        if (dfn[i] == -1) tarjan(i);
    for (int i = 1;i <= N;++i)
        if (belong[i] == belong[i+N]) return false;
    return true;
}

void getmap(int limit) {
    ecnt = 0;
    memset(head, -1,sizeof head);
    for (int i = 1;i <= A;++i) {
        add(hate[i].x, hate[i].y + N);
        add(hate[i].y, hate[i].x + N);

        add(hate[i].x + N, hate[i].y);
        add(hate[i].y + N, hate[i].x);
    }

    for (int i = 1;i <= B;++i) {
        add(like[i].x, like[i].y);
        add(like[i].y, like[i].x);

        add(like[i].x + N, like[i].y + N);
        add(like[i].y + N, like[i].x + N);
    }

    for (int i = 1;i <= N;++i) {
        for (int j = i + 1;j <= N;++j) {
            if (dis[i][N+1] + dis[j][N+1] > limit) {//不能一同食堂
                add(i, j + N);
                add(j, i + N);
            }
            if (dis[i][N+2] + dis[j][N+2] > limit) {
                add(i + N, j);
                add(j + N, i);
            }
            if (dis[i][N+1] + dis[j][N+2] + dis[N+1][N+2] > limit) {
                add(i, j);
                add(j+N, i+N);
            }
            if (dis[i][N+2] + dis[j][N+1] + dis[N+1][N+2] > limit) {
                add(i+N,j+N);
                add(j, i);
            }
        }
    }
}
int main(int argc, const char * argv[])
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    int T_T;
    scanf("%d", &T_T);
    while(T_T--) {
        scanf("%d%d%d", &N, &A, &B);
        s1.read(), s2.read();
        dis[N+1][N+2] = dist(s1, s2);
        for (int i = 1;i <= N;++i)
        {
            p[i].read();
            dis[i][N+1] = dist(p[i], s1);
            dis[i][N+2] = dist(p[i], s2);
        }
        for (int i = 1;i <= A;++i)
            hate[i].read();
        for (int i = 1;i <= B;++i)
            like[i].read();
        int low = 0, high = 50000000;
        int ans = -1;
        while(low <= high) {
            int mid = (low + high) / 2;
            getmap(mid);
            if (ok()) {
                ans = mid;
                high = mid - 1;
            }else low = mid + 1;
        }
        printf("%d\n", ans);
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}

E.Division

题意

分析

套路,缩点+匹配
const int N = 5000 + 10;
const int M = 100000 + 10;
int n, m;
int head[N], pnt[M], nxt[M], cnt;
void init() {
    memset(head, -1,sizeof head);
    cnt = 0;
}
void addedge(int u,int v) {
    pnt[cnt] = v, nxt[cnt] = head[u],head[u] = cnt++;
}
int dfn[N], low[N], belong[N];
int Times;
stack<int> st;
int scc;
void Tarjan(int u) {
    dfn[u] = low[u] = ++Times;
    st.push(u);
    for (int i = head[u];~i;i = nxt[i]) {
        int v = pnt[i];
        if (!dfn[v]) {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }else if (!belong[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        ++scc;
        while(true) {
            int x = st.top();
            st.pop();
            belong[x] = scc;
            if (x == u) break;
        }
    }
}
vector<int> G[N];

int linker[N];
bool vis[N];

bool dfs(int u) {
    for (int i = 0;i < G[u].size();++i) {
        int v = G[u][i];
        if (vis[v]) continue;
        vis[v] = true;
        if (linker[v] == -1 || dfs(linker[v])) {
            linker[v] = u;
            return true;
        }
    }
    return false;
}

int Hungary(){
    int ans = 0;
    memset(linker, -1,sizeof linker);
    for (int i = 1;i <= scc;++i) {
        memset(vis, false,sizeof vis);
        if (dfs(i)) ans++;
    }
    return ans;
}

int main()
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d",&n,&m);
        init();
        int u, v;
        for (int i = 1;i <= m;++i) {
            scanf("%d%d",&u,&v);
            addedge(u, v);
        }
        memset(dfn, 0,sizeof dfn);
        memset(belong, 0,sizeof belong);
        scc = Times = 0;
        for (int i = 1;i <= n;++i) G[i].clear();
        for (int i = 1;i <= n;++i) if (!dfn[i]) Tarjan(i);
        for (int i = 1;i <= n;++i) {
            for (int j = head[i];~j;j = nxt[j]) {
                int v = pnt[j];
                if (belong[i] != belong[v]) {
                    G[belong[i]].push_back(belong[v]);
                }
            }
        }
        printf("%d\n", scc - Hungary());
    }
    return 0;
}

F.meixiuxiu学图论

题意

求所有环中的最大边权的最小值。

分析

可以两种做法。
1.二分结果然后scc.
2.最小生成树的应用.
说第二种吧。。。把边排序,然后合并第一个形成的环的最后一天边就是答案。
const int maxn = 5e5 + 10;
const int maxm = 2e6 + 10;
struct Edge {
    int u, v, c;
    Edge() {}
    Edge(int u,int v,int c) {
        this->u = u;
        this->v = v;
        this->c = c;
    }
    bool operator < (const Edge& rhs) const {
        return c < rhs.c;
    }
    void read() {
        scanf("%d%d%d", &u, &v, &c);
    }
}e[maxm];
int f[maxn];
int find(int x) {
    return f[x] == -1?x : f[x] = find(f[x]);
}
int main(int argc, const char * argv[])
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    int n , m;
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for (int i = 0;i < m;++i)
            e[i].read();
        sort(e,e+m);
        memset(f, -1,sizeof f);
        int ans = -1;
        for (int i = 0;i < m;++i) {
            int t1 = find(e[i].u);
            int t2 = find(e[i].v);
            if (t1 != t2) {
                f[t1] = t2;
            }else if (ans == -1) ans = e[i].c;
        }
        if (ans == -1) puts("No solution!");
        else printf("%d\n", ans);
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}

G.最短路

题意

分析

问的是最短路的条数,且任意两条路不重复。首先每条路径都是最短的,所以就建立最短路树。然后就是最大流算法了。并附上最大流模版。。。
const int maxn = 1010;
vector<ii> G[2][maxn];//v, cost
int n, m;
int dis1[maxn], dis2[maxn], in[maxn];
void spfa(int s,int t,int* d, int o) {
    for (int i = 1;i <= n;++i)
        d[i] = INF;
    memset(in, 0,sizeof in);
    d[s] = 0;
    queue<int> que;
    que.push(s);
    while(!que.empty()) {
        int u = que.front();
        que.pop();
        in[u] = 0;
        for (int i = 0;i < G[o][u].size();++i) {
            int v = G[o][u][i].first;
            int cost = G[o][u][i].second;
            if (d[v] > d[u] + cost) {
                d[v] = d[u] + cost;
                if (in[v] == 0) {
                    in[v] = 1;
                    que.push(v);
                }
            }
        }
    }
}
struct Edge{
    int from, to, cap, flow;
    Edge(){}
    Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){}
};
struct ISAP{
    int p[maxn], num[maxn], cur[maxn], d[maxn];
    int s, t, n, m;
    bool vis[maxn];

    vector<int> G[maxn];
    vector<Edge> edges;

    void init(int n) {
        this->n = n;
        for (int i = 0;i <= n;++i) {
            G[i].clear();
            d[i] = INF;
        }
        edges.clear();
    }

    void addedge(int from,int to,int cap) {
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));
        m = (int)edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }

    bool bfs() {
        memset(vis, false,sizeof vis);

        queue<int> que;
        d[t] = 0;
        vis[t] = true;
        que.push(t);

        while(!que.empty()) {
            int u = que.front();
            que.pop();

            for (int i = 0;i < G[u].size();++i) {
                Edge& e = edges[G[u][i]^1];
                if (e.cap > e.flow && !vis[e.from]) {
                    vis[e.from] = true;
                    d[e.from] = d[u] + 1;
                    que.push(e.from);
                }
            }
        }
        return vis[s];
    }

    int Augment() {
        int u = t, flow = INF;
        while(u != s) {
            Edge& e = edges[p[u]];
            flow = min(flow, e.cap - e.flow);
            u = edges[p[u]].from;
        }

        u = t;
        while(u != s) {
            edges[p[u]].flow += flow;
            edges[p[u]^1].flow -= flow;
            u = edges[p[u]].from;
        }
        return flow;
    }

    int MaxFlow(int s,int t) {
        this->s = s,this->t = t;
        int ret = 0;
        bfs();
        if (d[s] >= n) return 0;

        memset(num, 0,sizeof num);
        memset(cur, 0,sizeof cur);
        for (int i = 0;i < n;++i) {
            if (d[i] < INF) num[d[i]]++;
        }
        int u = s;

        while(d[s] < n) {

            if (u == t) {
                ret += Augment();
                u = s;
            }

            bool ok = false;
            for (int i = cur[u];i < G[u].size();++i) {
                Edge& e = edges[G[u][i]];
                if (e.cap > e.flow && d[u] == d[e.to] + 1) {
                    ok = true;
                    p[e.to] = G[u][i];
                    cur[u] = i;
                    u = e.to;
                    break;
                }
            }

            if (!ok) {
                int Min = n - 1;
                for (int i = 0;i < G[u].size();++i) {
                    Edge& e = edges[G[u][i]];
                    if (e.cap > e.flow) Min = min(Min, d[e.to]);
                }
                if (--num[d[u]] == 0) break;
                num[d[u] = Min + 1]++;
                cur[u] = 0;
                if (u != s) u = edges[p[u]].from;
            }
        }
        return ret;
    }
}solve;
int main(int argc, const char * argv[])
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    int t;
    cin >> t;
    while(t--) {
        scanf("%d%d", &n, &m);
        for (int i = 1;i <= n;++i)
            G[0][i].clear(), G[1][i].clear();
        int u, v, c;
        for (int i = 1;i <= m;++i) {
            scanf("%d%d%d", &u, &v, &c);
            G[0][u].push_back(ii(v, c));
            G[1][v].push_back(ii(u, c));
        }
        int st, ed;
        scanf("%d%d", &st, &ed);
        spfa(st, ed, dis1, 0);
        spfa(ed, st, dis2, 1);
        int limit = dis1[ed];
        if (limit == INF) {
            printf("%d\n", 0);
            continue;
        }

        solve.init(n);

        for (int i = 1;i <= n;++i) {
            for (int j = 0;j < G[0][i].size();++j) {
                int k = G[0][i][j].first;
                if (dis1[i] != INF && dis2[k] != INF && dis1[i] + dis2[k] + G[0][i][j].second == limit) {
                    solve.addedge(i, k, 1);
                }
            }
        }
        int ans = solve.MaxFlow(st, ed);
        printf("%d\n", ans);
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}

H.NightMare2

题意

从1号点到n号点在k的时间内逃出去的前提下,能带走的最大价值珠宝。

分析

又是一种套路。二分答案,然后跑最短路,看能不能逃出去。
/*****************************************
Author      :Crazy_AC(JamesQi)
Time        :2016
File Name   :
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---‘\____
             .‘  \|     |//  `.
            /  \|||  :  |||//             /  _||||| -:- |||||-             |   | \\  -  /// |   |
           | \_|  ‘‘\---/‘‘  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .‘  /--.--\  `. . __
      ."" ‘<  `.___\_<|>_/___.‘  >‘"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-‘======
                   `=---=‘
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
*****************************************/
// #pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <climits>
using namespace std;
#define MEM(x,y) memset(x, y,sizeof x)
#define pk push_back
#define lson rt << 1
#define rson rt << 1 | 1
#define bug cout << "BUG HERE\n"
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> ii;
typedef pair<ii,int> iii;
const double eps = 1e-8;
const double pi = 4 * atan(1);
const long long inf = 1e20 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int nCase = 0;
int dcmp(double x){//精度正负、0的判断
    if (fabs(x) < eps) return 0;
    return x < 0?-1:1;
}
inline int read(){
    char c = getchar();
    while (!isdigit(c)) c = getchar();
    int x = 0;
    while (isdigit(c)) {
        x = x * 10 + c - ‘0‘;
        c = getchar();
    }
    return x;
}
const int maxn = 1e4 + 10;
struct Edge {
    int v, limit, cost;
    Edge() {}
    Edge(int v,int limit, int cost) {
        this->v = v;
        this->limit = limit;
        this->cost = cost;
    }
};
vector<Edge> G[maxn];
int n, m, k;
struct node {
    int p;
    long long cost;
    node() {}
    node(int p,long long cost) {
        this->p = p;
        this->cost = cost;
    }
    bool operator < (const node& rhs) const {//小的优先
        return cost > rhs.cost;
    }
};
long long dis[maxn];
int vis[maxn];
bool solve(int limit) {
    // memset(dis, INF,sizeof dis);
    for (int i = 1;i <= n;++i)
        dis[i] = inf;
    memset(vis, 0,sizeof vis);
    dis[1] = 0;
    priority_queue<node> que;
    que.push(node(1, 0));
    while(!que.empty()) {
        node temp = que.top();
        que.pop();
        int u = temp.p;
        if (vis[u]) continue;
        vis[u] = 1;
        for (int i = 0;i < G[u].size();++i) {
            if (G[u][i].limit < limit) continue;
            int v = G[u][i].v;
            long long cost = G[u][i].cost;
            if (dis[v] > dis[u] + cost) {
                dis[v] = dis[u] + cost;
                que.push(node(v, dis[v]));
            }
        }
    }
    return dis[n] <= k;
}

int main(int argc, const char * argv[])
{
    freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    int t;
    scanf("%d",&t);
    while(t--) {
        scanf("%d%d%d", &n, &m, &k);
        for (int i = 1;i <= n;++i)
            G[i].clear();
        int u, v, l, c;
        int high = 0, low = INF;
        for (int i = 1;i <= m;++i) {
            scanf("%d%d%d%d", &u, &v, &l, &c);
            G[u].push_back(Edge(v, l, c));
            G[v].push_back(Edge(u, l, c));
            high = max(high, l);
            low = min(low, l);
        }
        int ans = -1;
        while(low <= high) {
            int mid = (high + low) >> 1;
            if (solve(mid)) {
                low = mid + 1;
                ans = mid;
            }else high = mid - 1;
        }
        if (solve(low)) ans = max(ans, low);
        if (solve(high)) ans = max(ans, high);
        if (ans == -1) puts("Poor RunningPhoton!");
        else printf("%d\n", ans);
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}

I.玛雅,好简单

题意

分析

求无向图桥边的模版题。。。。
/*****************************************
Author      :Crazy_AC(JamesQi)
Time        :2016
File Name   :
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---‘\____
             .‘  \|     |//  `.
            /  \|||  :  |||//             /  _||||| -:- |||||-             |   | \\  -  /// |   |
           | \_|  ‘‘\---/‘‘  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .‘  /--.--\  `. . __
      ."" ‘<  `.___\_<|>_/___.‘  >‘"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-‘======
                   `=---=‘
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
*****************************************/
// #pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <climits>
using namespace std;
#define MEM(x,y) memset(x, y,sizeof x)
#define pk push_back
#define lson rt << 1
#define rson rt << 1 | 1
#define bug cout << "BUG HERE\n"
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> ii;
typedef pair<ii,int> iii;
const double eps = 1e-8;
const double pi = 4 * atan(1);
const int inf = 1 << 30;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int nCase = 0;
int dcmp(double x){//精度正负、0的判断
    if (fabs(x) < eps) return 0;
    return x < 0?-1:1;
}
inline int read(){
    char c = getchar();
    while (!isdigit(c)) c = getchar();
    int x = 0;
    while (isdigit(c)) {
        x = x * 10 + c - ‘0‘;
        c = getchar();
    }
    return x;
}
const int maxn = 10010;
vector<int> G[maxn];
int dfn[maxn], low[maxn], depth;
bool in[maxn];
int cnt;
stack<int> st;
void dfs(int u,int fa) {
    dfn[u] = low[u] = ++depth;
    st.push(u);
    in[u] = true;
    int first = 1;
    for (int i = 0;i < G[u].size();++i) {
        int v = G[u][i];
        if (first && v == fa) {
            first = 0;
            continue;
        }
        if (dfn[v] == -1) {
            dfs(v, u);
            if (low[v] > dfn[u]) cnt++;
            low[u] = min(low[u], low[v]);
        }else if (in[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        while(true) {
            int x = st.top();
            st.pop();
            in[x] = false;
            if (x == u) break;
        }
    }
}
int n, m;
void solve() {
    memset(dfn, -1,sizeof dfn);
    cnt = depth = 0;
    for (int i  =1;i <= n;++i)
        if (dfn[i] == -1) dfs(i, -1);
    printf("Case %d: %d\n", ++nCase, cnt);
}
int main(int argc, const char * argv[])
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for (int i = 1;i <= n;++i)
            G[i].clear();
        int u, v;
        for (int i = 1;i <= m;++i) {
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        solve();
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}

J.An Easy Problem

题意

就是选最少的人进行路径覆盖

分析

先闭包传递,然后二分图匹配最小路径覆盖。
const int N = 1010;
vector<int> G1[N], G2[N], G3[N];//原图,扩张图,缩点后的图。
bool vis[N];
int pre[N], low[N], Belong[N], scc_cnt, Times;
int n, m;
stack<int> st;
void Tarjan(int u) {
    pre[u] = low[u] = ++Times;
    st.push(u);
    for (int i = 0;i < (int)G2[u].size();++i) {
        int v = G2[u][i];
        if (!pre[v]) {
            Tarjan(v);
            low[u] = min(low[u], low[v]);
        }else if (!Belong[v]) low[u] = min(low[u], pre[v]);
    }
    if (pre[u] == low[u]) {
        scc_cnt++;
        while(true) {
            int x = st.top();
            st.pop();
            Belong[x] = scc_cnt;
            if (x == u) break;
        }
    }
}
void FindSCC() {
    memset(pre, 0,sizeof pre);
    memset(Belong, 0,sizeof Belong);
    Times = scc_cnt = 0;
    for (int i = 1;i <= n;++i) {
        if (!pre[i]) Tarjan(i);
    }
}
void BFS(int st) {
    queue<int> que;
    que.push(st);
    memset(vis, false,sizeof vis);
    vis[st] = true;
    while(!que.empty()) {
        int u = que.front();
        que.pop();
        for (int i = 0;i < G1[u].size();++i) {
            int v = G1[u][i];
            if (vis[v]) continue;
            vis[v] = true;
            G2[st].push_back(v);//对原图进行这种扩展是不会形成可行环的。
            que.push(v);
        }
    }
}
void Initation() {
    for (int i = 1;i <= n;++i)
        BFS(i);
}
void Input() {
    scanf("%d%d",&n,&m);
    for (int i = 1;i <= n;++i)
        G1[i].clear(),G2[i].clear(),G3[i].clear();
    int u, v;
    for (int i = 1;i <= m;++i) {
        scanf("%d%d",&u,&v);
        G1[u].push_back(v);
    }
}
void Trans() {
    for (int u = 1;u <= n;++u) {
        for (int i = 0;i < G2[u].size();++i) {
            int v = G2[u][i];
            if (Belong[u] != Belong[v])
                G3[Belong[u]].push_back(Belong[v]);
        }
    }
}
int linker[N];
bool Search(int u) {
    for (int i = 0;i < G3[u].size();++i) {
        int v = G3[u][i];
        if (vis[v]) continue;
        vis[v] = true;
        if (linker[v] == -1 || Search(linker[v])) {
            linker[v] = u;
            return true;
        }
    }
    return false;
}
int Hungary() {
    int ret = 0;
    memset(linker, -1,sizeof linker);
    for (int i = 1;i <= scc_cnt;++i) {
        memset(vis, false,sizeof vis);
        if (Search(i)) ret++;
    }
    return ret;
}
int main()
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    int t, icase = 0;
    scanf("%d",&t);
    while(t--) {
        Input();
        Initation();
        FindSCC();
        // printf("SCC = %d\n", scc_cnt);
        // for (int i = 1;i <= n;++i)
            // printf("%d ", Belong[i]);
        // puts("");
        Trans();

        // for (int i = 1;i <= n;++i) {
            // printf("<%d>::", i);
            // for (int j = 0;j < G2[i].size();++j)
                // printf("%d ", G2[i][j]);
            // puts("");
        // }

        // for (int i = 1;i <= scc_cnt;++i) {
        //  printf("i = %d::::", i);
        //  for (int j = 0;j < G3[i].size();++j)
        //      printf("%d ",G3[i][j]);
        //  puts("");
        // }
        printf("Case %d: %d\n", ++icase, scc_cnt - Hungary());
    }
    return 0;
}

K.投票

题意

投票是单向的且具有传递性,求获得票数最多的人。。。

分析

首先一个连通分量里面的人获得的票数肯定是一样的,然后缩点成DAG题,反向建边,再从入读为0的点开始搜索。
/*****************************************
Author      :Crazy_AC(JamesQi)
Time        :2016
File Name   :
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---‘\____
             .‘  \|     |//  `.
            /  \|||  :  |||//             /  _||||| -:- |||||-             |   | \\  -  /// |   |
           | \_|  ‘‘\---/‘‘  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .‘  /--.--\  `. . __
      ."" ‘<  `.___\_<|>_/___.‘  >‘"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-‘======
                   `=---=‘
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
*****************************************/
// #pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <climits>
using namespace std;
#define MEM(x,y) memset(x, y,sizeof x)
#define pk push_back
#define lson rt << 1
#define rson rt << 1 | 1
#define bug cout << "BUG HERE\n"
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> ii;
typedef pair<ii,int> iii;
const double eps = 1e-8;
const double pi = 4 * atan(1);
const int inf = 1 << 30;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
int nCase = 0;
int dcmp(double x){//精度正负、0的判断
    if (fabs(x) < eps) return 0;
    return x < 0?-1:1;
}
inline int read(){
    char c = getchar();
    while (!isdigit(c)) c = getchar();
    int x = 0;
    while (isdigit(c)) {
        x = x * 10 + c - ‘0‘;
        c = getchar();
    }
    return x;
}
const int maxn = 5010;
const int maxm = 30010;
int head[maxn], pnt[maxm], nxt[maxm], ecnt;
int n, m;
int dfn[maxn], low[maxn], in[maxn], depth;
int belong[maxn], block;
int cnt1[maxn],cnt2[maxn], cnt3[maxn];
stack<int> st;
void dfs(int u) {
    dfn[u] = low[u] = ++depth;
    in[u] = 1;
    st.push(u);
    for (int i = head[u]; ~i; i = nxt[i]) {
        int v = pnt[i];
        if (dfn[v] == -1) {
            dfs(v);
            low[u] = min(low[u], low[v]);
        }else if (in[v]) low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        block++;
        while(true) {
            int x = st.top();
            st.pop();
            in[x] = 0;
            belong[x] = block;
            cnt1[block]++;
            if (x == u) break;
            // cnt1[block]++;
        }
    }
}
void find_scc() {
    memset(dfn, -1,sizeof dfn);
    memset(in, 0,sizeof in);
    memset(cnt1, 0,sizeof cnt1);//scc
    depth = block = 0;
    for (int i = 0;i < n;++i)
        if (dfn[i] == -1) dfs(i);
}
vector<int> G[maxn];
int mark[maxn];
int search(int u) {
    int sum = 0;
    for (int i = 0;i < G[u].size();++i) {
        int v = G[u][i];
        if (mark[v]) continue;
        mark[v] = 1;
        sum += cnt1[v];
        sum += search(v);
    }
    return sum;
}
void solve() {
    for (int i = 1;i <= block;++i)
        G[i].clear();
//rebuild new graph
    for (int u = 0;u < n;++u) {
        for (int i = head[u]; ~i;i = nxt[i]) {
            int v = pnt[i];
            if (belong[u] != belong[v]) {
                G[belong[v]].push_back(belong[u]);
                in[belong[u]]++;
            }
        }
    }
    memset(cnt2, 0,sizeof cnt2);
    for (int i = 1;i <= block;++i) {
        if (in[i] == 0) {
            memset(mark, 0,sizeof mark);
            cnt2[i] = search(i);
        }
    }
    int Max = 0;
    for (int i = 0;i < n;++i) {
        cnt3[i] = cnt1[belong[i]] + cnt2[belong[i]] - 1;
        // cout << "cnt3 = " << cnt3[i] << endl;
        Max = max(Max, cnt3[i]);
    }
    printf("Case %d: %d\n", ++nCase, Max);
    int first = 1;
    for (int i = 0;i < n;++i) {
        if (cnt3[i] == Max) {
            if (first) first = 0;
            else printf(" ");
            printf("%d", i);
        }
    }
    printf("\n");
}
int main(int argc, const char * argv[])
{
    // freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    // clock_t _ = clock();

    int t;
    cin >> t;
    while(t--) {
        scanf("%d %d", &n, &m);
        memset(head, -1,sizeof head), ecnt = 0;
        int u, v;
        for (int i = 1;i <= m;++i) {
            scanf("%d %d", &u, &v);
            pnt[ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt++;
        }
        find_scc();
        solve();
    }

    // printf("\nTime cost: %.2fs\n", 1.0 * (clock() - _) / CLOCKS_PER_SEC);
    return 0;
}
时间: 2024-10-27 07:24:24

联合训练图论场的相关文章

2014多校联合训练第一场(组队训练)

这是我.potaty.lmz第二次训练,毕竟经验不足,加上水平不够,导致我们各种被碾压. A - Couple doubi: 这道题是道比较水的数论.但我们都没想出来要怎么做.后来是potaty提议打个表看看,然后lmz打出表后发现了规律.我还没细看,待研究后再补全. D - Task: 这道题一看就知道是个贪心(现在只要是有deadline的题我都觉得是贪心了).虽然想出来了,但还是不会严格证明为什么只要取满足task的且y最小(y相等时x最小)的machine就行了. 我的做法是把所有mac

【补题】多校联合训练第二场

第二场 1001 Is Derek lying? Problem Description Derek and Alfia are good friends.Derek is Chinese,and Alfia is Austrian.This summer holiday,they both participate in the summer camp of Borussia Dortmund.During the summer camp,there will be fan tests at i

2015多校联合训练第一场Tricks Device(hdu5294)

题意:给一个无向图,给起点s,终点t,求最少拆掉几条边使得s到不了t,最多拆几条边使得s能到t 思路: 先跑一边最短路,记录最短路中最短的边数,总边数-最短边数就是第二个答案 第一个答案就是在最短路里面求最小割,也就是求最大流,然后根据最短路在建个新图,权为1,跑一边网络流 模板题,以后就用这套模板了 #include <iostream> #include <cstdio> #include <cstring> #include <queue> #incl

2015年多校联合训练第一场OO’s Sequence(hdu5288)

题意:给定一个长度为n的序列,规定f(l,r)是对于l,r范围内的某个数字a[i],都不能找到一个对应的j使得a[i]%a[j]=0,那么l,r内有多少个i,f(l,r)就是几.问所有f(l,r)的总和是多少. 公式中给出的区间,也就是所有存在的区间. 思路:直接枚举每一个数字,对于这个数字,如果这个数字是合法的i,那么向左能扩展的最大长度是多少,向右能扩展的最大长度是多少,那么i为合法的情况就是左长度*右长度(包含i且i是合法的区间总数). 统计左长度可以判断a[i]的约数是否在前面出现过-因

2015多校联合训练第一场Assignment(hdu5289)三种解法

题目大意:给出一个数列,问其中存在多少连续子序列,子序列的最大值-最小值 #include <iostream> #include <cstdio> #include <algorithm> #include <string> #include <cmath> using namespace std; int maxsum[100000][30]; int minsum[100000][30]; int a[100000]; int n,k; v

【补题】多校联合训练第一场

1001  Add More Zero Problem Description There is a youngster known for amateur propositions concerning several mathematical hard problems.Nowadays, he is preparing a thought-provoking problem on a specific type of supercomputer which has ability to s

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&#39;s problem(manacher+二分/枚举)

HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分相同,第一部分与第二部分对称. 现在给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法,求出以第i个点为中心的回文串长度,记录到数组p中 要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样. 因为我们已经记录下来以

HDU 5371 (2015多校联合训练赛第七场1003)Hotaru&amp;#39;s problem(manacher+二分/枚举)

pid=5371">HDU 5371 题意: 定义一个序列为N序列:这个序列按分作三部分,第一部分与第三部分同样,第一部分与第二部分对称. 如今给你一个长为n(n<10^5)的序列,求出该序列中N序列的最大长度. 思路: 来自官方题解:修正了一些题解错别字(误 先用求回文串的Manacher算法.求出以第i个点为中心的回文串长度.记录到数组p中 要满足题目所要求的内容.须要使得两个相邻的回文串,共享中间的一部分,也就是说.左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也

HDOJ多校联合第五场

1001 题意:求逆序对,然后交换k次相邻的两个数,使得剩下的逆序对最少. 分析:题目用到的结论是:数组中存在一对逆序对,那么可以通过交换相邻两个数使得逆序对减少1,交换k次,可以最多减少k个. 嘉定ai>aj,i < j,如果ai,aj相邻的,那么显然可以通过交换减少1:不相邻的情况, 考虑ak,k = j-1; #11:ak > aj,那么ak,aj构成逆序对,交换后逆序对减少1: #12:ak<=aj,那么ai,ak构成逆序对,问题转化为更小规模,可以通过同样的方法进一步分析