• “树和图”非线性结构的有关知识


    树的一些性质

    二叉树:第i层至多有2^(i-1)个节点,至少有1个;深度为k的二叉树至多有2^k-1个节点,至少有k个节点。

    节点总数=边数(总度数)+1=二度点*2+一度点+1=二度点+一度点+叶子节点 推出 二度点+1=叶子节点个数

    完全二叉树:n个结点的叶子节点个数有 n/2 向上取整个。深度为logn/log2+1。顺序存储时若1存根节点,则2n为左结点,2n+1为右结点。

    二叉链表:n个结点共有2n个指针域,其中n-1个表示结构,n+1个空指针。利用起来这些空指针为线索二叉树。

    先序/中序/后序线索二叉树:

    LTag=0, lchild域指向左孩子; LTag=1, lchild域指向其前驱

    RTag=0, rchild域指向右孩子; RTag=1, rchild域指向其后继

    树的二叉链表表示法:左节点为子节点的第一个,可以不断找右节点找到此层的所有节点。

     

    一,最短(长)路径

    1.优先队列优化的Prim

    贪心思想,维护到每个点最小距离,其中最小的必然在最短路径上。

    int dis[MAXN];
    int T, n, m, c, tmp;
    struct Edge {
        int to, nxt;
        int cap;
    } edge[MAXN * 3];
    
    void addedge(int u, int v, int w) {
        edge[tot].to = v;
        edge[tot].cap = w;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    
    struct point { //到每个点的状态
        int v, dis;
        point(int a, int b) {
            v = a, dis = b;
        }
        bool operator <(const point &tmp) const {
            return dis > tmp.dis;
        }
    };
    void dijkstra() {
        priority_queue<point> q;
        for (int i = 1; i <= n; i++)
            dis[i] = INF, vis[i] = 0; //vis表示是否已经确定最短路
        dis[1] = 0;
        while (!q.empty())
            q.pop();
        q.push(point(1, 0));
        while (!q.empty()) {
            point cur = q.top();
            q.pop();
            if (vis[cur.v])
                continue;
            vis[cur.v] = 1;
            for (int i = head[cur.v]; i != -1; i = edge[i].nxt) {
                int v = edge[i].to;
                if (v == cur.v || vis[v])
                    continue;
                if (dis[v] > dis[cur.v] + edge[i].cap) {
                    dis[v] = dis[cur.v] + edge[i].cap;
                    q.push(point(v, dis[v]));
                }
            }
        }
    }

    2.SPFA

    动态规划的思想,用队列优化,每次只对更小的距离继续动规。

    int dis[MAXN];
    bool vis[MAXN];
    int spfa() { // 求最长路
        queue<int> Q;
        for (int i = low; i <= high; i++) {
            dis[i] = -INF;
            vis[i] = 0;
        }
        dis[low] = 0;
        vis[low] = 1;
        Q.push(low);
        while (!Q.empty()) {
            int u = Q.front();
            Q.pop();
            vis[u] = 0;
            for (int i = head[u]; i != -1; i = edge[i].next) {
                int v = edge[i].v;
                if (dis[u] + edge[i].w > dis[v]) {
                    dis[v] = dis[u] + edge[i].w;
                    if (!vis[v]) {
                        Q.push(v); //如果一个点入队超过点个数次,说明有负环
                        vis[v] = 1;
                    }
                }
            }
        }
        return dis[high];
    }

    3.floyd

    int G[MAXN][MAXN], dis[MAXN][MAXN], pre[MAXN][MAXN];
    
    void floyd(int n) {
        int i, j, k;
        for (i = 0; i < n; i++)
            for (j = 0; j < n; j++)
                dis[i][j] = G[i][j], pre[i][j] = (i == j) ? -1 : i;
        for (k = 0; k < n; k++)
        for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            if (dis[i][k] != INF && dis[k][j] != INF&& (dis[i][j] == INF || dis[i][k] + dis[k][j] < dis[i][j]))
            dis[i][j] = dis[i][k] + dis[k][j], pre[i][j] = pre[k][j];
    }
    //不保存原图,不联通G为0
    void floyd(int n) {
        int k, i, j;
        //memcpy(NG, G, sizeof(G));
        for (k = 1; k <= n; k++)
        for (i = 1; i <= n; i++)
        for (j = 1; j <= n; j++)
            if (G[i][k] && G[k][j]&& (!G[i][j] || G[i][j] > G[i][k] + G[k][j]))
                G[i][j] = G[i][k] + G[k][j];
    }

    二,最小(大)生成树/最小(大)生成森林

    1.优先队列优化Prim

    int Prim(int n) {
        priority_queue<Edge> Q;
        bool vis[MAXN];
        memset(vis, 0, sizeof(vis));
        vector<int> vSelected;
        int nDoneNum = 0;
        int tot_w = 0;
        Q.push(Edge(0, 0));
        while (nDoneNum < n && !Q.empty()) {
            Edge Min = Q.top();
            Q.pop();
            if (vis[Min.to])
                continue;
            tot_w += Min.w;
            vis[Min.to] = 1;
            nDoneNum++;
            vSelected.push_back(Min.to);
            for (int i = head[Min.to]; i != -1; i = edge[i].nxt) {
                int v = edge[i].to;
                if (v == Min.to || vis[v])
                    continue;
                Q.push(edge[i]);
            }
        }
        if (nDoneNum < n)
            return -1; //图不连通
        return tot_w;
    }

    2.kruskal 最小(大)生成森林 每个联通块都保持联通性..并且总花费最小

    int p[MAXN];
    void Init_Set() {
        for (int i = 0; i <= MAXN; i++) {
            p[i] = i;
        }
    }
    int find(int x) {
        if (x != p[x])
            p[x] = find(p[x]);
        return p[x];
    }
    int kruscal(int n) {
        int x, y;
        int edgeNum = 0;
        int tot_w = 0;
        Init_Set();
        sort(edge, edge + tot);
        for (int i = 0; i < tot; i++) {
            x = find(edge[i].from);
            y = find(edge[i].to);
            if (x != y) {
                edgeNum++;
                p[x] = y;
                tot_w += edge[i].w;
            }
        }
        return tot_w;
    }

    三,最短路和生成树的变化

    1.次短路(维护个数) POJ3463

    struct point {
        int v, dis;
        point(int a, int b) {
            v = a, dis = b;
        }
        bool operator <(const point &tmp) const {
            return dis > tmp.dis;
        }
    };
    void dijkstra(int s) {
        priority_queue<point> q;
        for (int i = 0; i <= n; i++) {
            dis[i] = dis2[i] = INF;
            cnt[i][0] = cnt[i][1] = vis[i][0] = vis[i][1] = 0;
        }
        dis[s] = 0;
        q.push(point(s, 0));
        cnt[s][0] = 1;
        while (!q.empty()) {
            point cur = q.top();
            q.pop();
            if (vis[cur.v][0] && vis[cur.v][1]) {
                continue;
            }
            int p;
            if (!vis[cur.v][0])
                vis[cur.v][0] = 1, p = 0;
            else
                vis[cur.v][1] = 1, p = 1;
            int Min_d = cur.dis;
            for (int i = head[cur.v]; i != -1; i = edge[i].nxt) {
                int v = edge[i].to;
                if (v == cur.v)
                    continue;
                if (dis[v] > Min_d + edge[i].cap) {
                    dis2[v] = dis[v];
                    cnt[v][1] = cnt[v][0];
                    dis[v] = Min_d + edge[i].cap;
                    cnt[v][0] = cnt[cur.v][p];
                    q.push(point(v, dis[v]));
                } else if (dis[v] == Min_d + edge[i].cap) {
                    cnt[v][0] += cnt[cur.v][p];
    //                q.push(point(v, dis[v])); WA点
                } else if (dis2[v] > Min_d + edge[i].cap) {
                    dis2[v] = Min_d + edge[i].cap;
                    cnt[v][1] = cnt[cur.v][p];
                    q.push(point(v, dis2[v]));
                } else if (dis2[v] == Min_d + edge[i].cap) {
                    cnt[v][1] += cnt[cur.v][p];
    //                q.push(point(v, dis2[v])); WA点
                }
            }
        }
    }

    2.第K短路

    建图时建一个反向图,从终点求到每个点的最短路作为AStar的估值。

    #define MAXN 1009
    #define INF 0x3fffffff
    int head[MAXN], head2[MAXN];
    bool vis[MAXN];
    int cnt[MAXN];
    int tot, tot2;
    int n, m;
    struct Statement {
        int v, d, h;
    };
    bool operator <(const Statement &a, const Statement &b) {
        return a.d + a.h > b.d + b.h;
    }
    int dis[MAXN];
    struct Edge {
        int to, nxt;
        int cap;
    } edge[300003], edge2[300003];
    void init() {
        tot = 0;
        memset(head, -1, sizeof(head));
        tot2 = 0;
        memset(head2, -1, sizeof(head));
    }
    void addedge(int u, int v, int cap) {
        edge[tot].to = v;
        edge[tot].cap = cap;
        edge[tot].nxt = head[u];
        head[u] = tot++;
    }
    void addedge2(int u, int v, int cap) {
        edge2[tot2].to = v;
        edge2[tot2].cap = cap;
        edge2[tot2].nxt = head2[u];
        head2[u] = tot2++;
    }
    struct point {
        int v, dis;
        point(int a, int b) {
            v = a, dis = b;
        }
    };
    bool operator <(const point &a, const point &b) {
        return a.dis > b.dis;
    }
    void dijkstra(int t) { //反向图从终点做最短路
        priority_queue<point> q;
        for (int i = 1; i <= n; i++)
            dis[i] = INF, vis[i] = 0; //vis表示是否已经确定最短路
        dis[t] = 0;
        q.push(point(t, 0));
        while (!q.empty()) {
            point cur = q.top();
            q.pop();
            if (vis[cur.v])
                continue;
            vis[cur.v] = 1;
            for (int i = head2[cur.v]; i != -1; i = edge2[i].nxt) {
                int v = edge2[i].to;
                if (v == cur.v || vis[v])
                    continue;
                if (dis[v] > dis[cur.v] + edge[i].cap) {
                    dis[v] = dis[cur.v] + edge[i].cap;
                    q.push(point(v, dis[v]));
                }
            }
        }
    }
    int Astar_Kth(int s, int t, int K) {
        Statement cur, nxt;
        priority_queue<Statement> Q;
        if(dis[s] == INF)
            return -1;
        memset(cnt, 0, sizeof(cnt));
        cur.v = s;
        cur.d = 0; //步数
        cur.h = dis[s];
        Q.push(cur);
    
        while (!Q.empty()) {
            cur = Q.top();
            Q.pop();
    
            cnt[cur.v]++;
            if (cnt[cur.v] > K)
                continue;
            if (cnt[t] == K)
                return cur.d; //终点弹出K次说明找到第K短的路径
            for (int i = head[cur.v]; i != -1; i = edge[i].nxt) {
                int v = edge[i].to;
                nxt.d = cur.d + edge[i].cap;
                nxt.v = v;
                nxt.h = dis[v];
                Q.push(nxt);
            }
        }
        return -1;
    }
    int main() {
        int u, v, w;
        int s, t, k;
        while (~scanf("%d%d", &n, &m)) {
            init();
            for (int i = 0; i < m; i++) {
                scanf("%d%d%d", &u, &v, &w);
                addedge(u, v, w);
                addedge2(v, u, w);
            }
            scanf("%d%d%d", &s, &t, &k);
            if (s == t)
                k++;
            dijkstra(t);
            printf("%d
    ", Astar_Kth(s, t, k));
        }
        return 0;
    }

    3.次小生成树

    //POJ 1679 求次小生成树权值和 首先求最小生成树T 然后从每个结点BFS遍历最小生成树T
    //更新二维数组Max_w[u][v]记录结点u到结点v的路劲上边的最大值(即最大边的值)
    //然后枚举不在T中的边(u,v),计算T- Max_w[u][v] + w(u,v)的最小值,即为次小生成树的权值
    
    int n, m;
    int Max_w[101][101];
    
    int tot;
    int head[MAXN];
    void init() {
        tot = 0;
        memset(head, -1, sizeof(head));
    }
    struct Edge {
        int from, to, nxt;
        int w;
        bool flag;
        Edge() {
            flag = 0;
        }
        Edge(int _to, int _w) :
                to(_to), w(_w) {
        }
        bool operator <(const Edge & tmp) const {
            return w < tmp.w; //换成<就是最大生成树
        }
    } edge[250000];
    
    vector<pair<int, int> > SMT[MAXN];
    int kruscal() {
        int x, y;
        int edgeNum = 0;
        int tot_w = 0;
        Init_Set();
        for (int i = 0; i <= n; i++)
            SMT[i].clear();
        sort(edge, edge + tot);
        for (int i = 0; i < tot; i++) {
            x = find(edge[i].from);
            y = find(edge[i].to);
            if (x != y) {
                edgeNum++;
                p[x] = y;
                SMT[edge[i].from].push_back(make_pair(edge[i].to, edge[i].w));
                SMT[edge[i].to].push_back(make_pair(edge[i].from, edge[i].w));
                edge[i].flag = 1;
                tot_w += edge[i].w;
            }
        }
        return tot_w;
    }
    struct Node {
        int to, Max;
    };
    void bfs(int root) {
        bool vis[MAXN];
        memset(vis, 0, sizeof(vis));
        queue<Node> que;
        Node now;
        now.Max = 0;
        now.to = root;
        que.push(now);
        vis[root] = 1;
        while (!que.empty()) {
            Node top = que.front();
            que.pop();
            for (size_t i = 0; i < SMT[top.to].size(); i++) {
                now.to = SMT[top.to][i].first;
                if (!vis[now.to] && now.to != top.to) {
                    now.Max = max(SMT[top.to][i].second, top.Max);
                    Max_w[root][now.to] = now.Max;
                    vis[now.to] = 1;
                    que.push(now);
                }
            }
        }
    }
    void second_MST() {
        int mst = kruscal();
        for (int i = 1; i <= n; i++)
            bfs(i);
        int smst = INF;
        for (int i = 0; i < m; i++) {
            if (!edge[i].flag)
                if (mst + edge[i].w - Max_w[edge[i].from][edge[i].to] < smst)
                    smst = mst + edge[i].w - Max_w[edge[i].from][edge[i].to];
        }
        if (smst == mst)
            printf("Not Unique!
    ");
        else
            printf("%d
    ", mst);
    }

    4.最小度生成树(有一个点的度不能超过K)

     

    先不计这个点求最小生成森林,产生的联通块数量就是最小度数,在这个最小度数上增加,每次找一条不在生成树上和最小度点相连的那些边中,连上去掉成环环上最大边这个增加值最小的选择相连。通过dfs可以预处理出每个点被成环后环上的最大边,和次小生成树的方法一致。这里dfs即不能走到父节点,也不能走到最小度点,不然就死循环WA

    #define MAXN 1009
    int head[MAXN], tot;
    int N, M, K;
    int ans;
    bool vis[MAXN];
    pair<int, int> prim(int u) {
        int Min_dis = 0x3fffffff, k = 0;
        priority_queue<Edge> Q;
        Edge cur;
        cur.u = u, cur.v = u, cur.w = 0;
        Q.push(cur);
        while (!Q.empty()) {
            cur = Q.top();
            Q.pop();
            if (vis[cur.v])
                continue;
            vis[cur.v] = 1;
            tree[cur.u][cur.v] = tree[cur.v][cur.u] = 1;
            ans += cur.w;
    
            for (int i = head[cur.v]; i != -1; i = edge[i].next)
                if (edge[i].v == 0 && Min_dis > edge[i].w) {
                    Min_dis = edge[i].w, k = cur.v;
                    break;
                }
            for (int i = head[cur.v]; i != -1; i = edge[i].next) {
                int v = edge[i].v;
                if (v == cur.v || vis[v])
                    continue;
                Q.push(edge[i]);
            }
        }
        return make_pair(k, Min_dis);
    }
    struct DEL {
        int u, v;
        int dis;
    } del[MAXN];
    void dfs(int cur, int fa) {
        for (int i = head[cur]; i != -1; i = edge[i].next) {
            int v = edge[i].v;
            if (v == 0 || v == fa || !tree[cur][v])
                continue;
            if (del[cur].dis < edge[i].w) {
                del[v].dis = edge[i].w;
                del[v].u = cur, del[v].v = v;
            } else {
                del[v] = del[cur];
            }
            dfs(v, cur);
        }
    }
    void solve() {
        memset(vis, 0, sizeof(vis));
        memset(tree, 0, sizeof(tree));
        memset(del, 0, sizeof(del));
        vis[0] = 1;
        for (int i = 1; i < N; i++) {
            if (vis[i])
                continue;
            K--;
            pair<int, int> k = prim(i);
            ans += k.second;
            tree[k.first][0] = tree[0][k.first] = 1;
            del[k.first].dis = k.second;
            del[k.first].u = 0, del[k.first].v = k.first;
            dfs(k.first, 0);
        }
        while (K--) {
            int Min_add = 0x3fffffff, k;
            for (int i = head[0]; i != -1; i = edge[i].next) {
                int v = edge[i].v;
                if (!tree[0][v]) {
                    if (Min_add > edge[i].w - del[v].dis)
                        Min_add = edge[i].w - del[v].dis, k = v;
                }
            }
            if (Min_add >= 0)
                break;
            ans += Min_add;
            tree[0][k] = tree[k][0] = 1;
            tree[del[k].u][del[k].v] = tree[del[k].v][del[k].u] = 0;
            dfs(k, 0);
        }
    }

     

    5.走K条边的floyd最短距离 属于矩阵相乘的应用

    #include<cstdio>
    #include<cstring>
    #define N 101
    struct Matrix {
        int edge[N][N];
    } map, tmp, res;
    int n, f[N * 10];
    Matrix mul(Matrix x, Matrix y) {
        memset(tmp.edge, 0x3f3f3f3f, sizeof(tmp.edge));
        int i, j, k;
        for (i = 1; i <= n; i++)
            for (j = 1; j <= n; j++)
                for (k = 1; k <= n; k++)
                    if (tmp.edge[i][j] > x.edge[i][k] + y.edge[k][j])
                        tmp.edge[i][j] = x.edge[i][k] + y.edge[k][j];
        return tmp;
    }
    void quickpow(int k) {
        memset(res.edge, 0x3f3f3f3f, sizeof(res.edge));
        for (int i = 1; i <= n; i++)
            res.edge[i][i] = 0;
        while (k) {
            if (k & 1)
                res = mul(res, map);
            map = mul(map, map);
            k >>= 1;
        }
    }
    int main() {
    //    freopen("data3.txt", "r", stdin);
        int k, t, s, e, len, u, v;
        scanf("%d%d%d%d", &k, &t, &s, &e);
        memset(map.edge, 0x3f3f3f3f, sizeof(map.edge));
        memset(f, -1, sizeof(f));
        int num = 1;
        for (int i = 0; i < t; i++) {
            scanf("%d%d%d", &len, &u, &v);
            if (f[u] == -1)    //离散化
                f[u] = num++;
            if (f[v] == -1)
                f[v] = num++;
            map.edge[f[u]][f[v]] = map.edge[f[v]][f[u]] = len;
        }
        n = num - 1;
        quickpow(k);
        printf("%d
    ", res.edge[f[s]][f[e]]);
        return 0;
    }
    #define N 109
    #define INF 0x7f
    int n, m;
    int dis[N][N][N];
    int vis[N][N], tmp[N][N];
    void init() {
        memset(vis, -1, sizeof(vis));
        memset(dis, 0x7f, sizeof(dis));
    }
    void floyd_k() {
        for (int k = 1; k <= n; k++)
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    if (vis[i][k] != -1 && vis[k][j] != -1) {
                        if (vis[i][j] == -1)
                            vis[i][j] = vis[i][k] + vis[k][j];
                        else
                            vis[i][j] = min(vis[i][j], vis[i][k] + vis[k][j]);
                    }
        for (int k = 1; k <= n; k++) { // 走k条边
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++) {
                    for (int h = 1; h <= n; h++) {
                        if (vis[i][h] == -1 || k < vis[i][h] || vis[h][j] == -1
                                || k < vis[h][j])
                            continue;
                        dis[i][j][k] = min(dis[i][j][k],
                                dis[i][h][k - 1] + dis[h][j][0]);
                    }
                }
        }
    }
    int main() {
        while (~scanf("%d%d", &n, &m)) {
            init();
            for (int i = 0; i < m; i++) {
                int u, v, w;
                scanf("%d%d%d", &u, &v, &w);
                dis[u][v][0] = w;
                vis[u][v] = 1;
            }
            floyd_k();
        }
        return 0;
    }

    四,最近公共祖先

    1.离线

    int indegree[MAX];
    int visit[MAX], father[MAX];
    vector<int> tree[MAX],Qes[MAX]; //树,查询
    int ancestor[MAX];//记录公共祖先
    void init(int n){  
        for(int i=1;i<=n;i++){
            father[i]=i;
            indegree[i]=visit[i]=0;
            tree[i].clear();
            Qes[i].clear();
        }
    }  
    void LCA(int u){
        ancestor[u]=u;
        for(int i=0;i<tree[u].size();i++){
            LCA(tree[u][i]);
            Union(u,tree[u][i]);
            ancestor[find(u)]=u;
        }
        visit[u]=1;
        for(int i=0;i<Qes[u].size();i++){  
            //如果已经访问了目标节点
            if(visit[Qes[u][i]])
                printf("%d
    ",ancestor[find(Qes[u][i])] );
        }
    }
    int main(){
        while(T--){
            scanf("%d",&n);
            init(n);
            for(i=0;i<n-1;i++){
                scanf("%d%d",&a,&b);
                tree[a].push_back(b);
                indegree[b]++;
            }
            scanf("%d%d",&a,&b);
            Qes[a].push_back(b);
            if(a!=b) Qes[b].push_back(a);
            for(i=1;i<=n;i++)
                if(indegree[i] == 0){
                    LCA(i);
                    break;
                }
        } 
    }

    2.倍增

    const int LOG = 20;
    struct node {
        int to, val, next;
    } edge[MAXN];
    int dis[MAXN];
    int po[MAXN], tol;
    int dep[MAXN];
    int p[MAXN][22];
    void dfs(int x, int fa, int depth, int cost) {
        dep[x] = depth;
        p[x][0] = fa;
        dis[x] = cost;
        for (int i = 1; i <= LOG; i++)
            p[x][i] = p[p[x][i - 1]][i - 1]; // 倍增,存的是节点x第2^i个祖先
        for (int i = po[x]; i; i = edge[i].next) {
            int y = edge[i].to;
            if (y != fa)
                dfs(y, x, depth + 1, cost + edge[i].val);
        }
    }
    int lca(int x, int y) { // 倍增求lca
        if (dep[x] > dep[y])
            swap(x, y);
        if (dep[x] < dep[y]) {
            int del = dep[y] - dep[x];
            for (int i = 1; i <= LOG; i++)
                if (del >> i & 1)
                    y = p[y][i]; //将y与x深度调整为相同
        }
        if (x != y) {
            for (int i = LOG - 1; i >= 0; i--)
                if (p[x][i] != p[y][i]) {
                    x = p[x][i];
                    y = p[y][i];
                }
            x = p[x][0], y = p[y][0];
        }
        return x;
    }
    //根据x,y的最近公共祖先求x到y路径上第K个点
    int up(int x, int k) { // 求节点x的第k个祖先编号
        for (int i = 0; i < LOG; i++)
            if (k >> i && (k >> i & 1))
                x = p[x][i];
        return x;
    }
    int path_k(int k, int x, int y) { // 求x到y路径上第k节点编号
        int ca = lca(x, y);
        if (dep[x] - dep[ca] + 1 >= k) {
            return up(x, k - 1);
        } else {
            k -= dep[x] - dep[ca];
            k = dep[y] - dep[ca] + 1 - k;
            return up(y, k);
        }
    }

     

    五,树链剖分

    每个点(或者边)有权值

    我们要做的就是询问两个点之间路径上各点(边)权值的最大、最小,权值和(就是线段树能干的)

    然后我们还要支持在线更改任意节点(边)的权值。

    我们要做的是轻重链剖分,首先我们看几个定义

    size:和SBT里的一样,size[i]为以该点为根节点的子树一共有几个节点。

    重儿子:一个节点当不为叶子节点的时候有且只有一个重儿子,重儿子为该点的儿子中size最大的,有多个最大时任选一个。

    重链:由根节点开始,每个点每次都访问自己的重儿子,一直访问到叶子节点,就组成了一条重链

         那么对于一个点的非重儿子来说,以他为根节点,可以重新访问出一条重链。

         重链几个明显的性质就是互不重合且所有重链覆盖所有点,重链之间由一条不在重链上的边,我们称作轻边。

    例图:红色的线画出的为重链,其中6号点自己为一条重链

    siz[v]表示以v为根的子树的节点数,dep[v]表示v的深度(根深度为1)
    top[v]表示v所在的链的顶端节点,fa[v]表示v的父亲
    son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子)
    w[v]表示v与其父亲节点的连边(姑且称为v的父边)在线段树中的位置

    struct Edge {
        int to, next;
    } edge[MAXN * 2];
    int head[MAXN], tot;
    int top[MAXN]; //top[v]表示v所在的重链的顶端节点
    int fa[MAXN]; //父亲节点
    int deep[MAXN]; //深度
    int num[MAXN]; //num[v]表示以v为根的子树的节点数
    int p[MAXN]; //p[v]表示v与其父亲节点的连边在线段树中的位置
    int fp[MAXN]; //和p数组相反
    int son[MAXN]; //重儿子:子节点中num[]最大的节点
    int pos; //形成线段树区间长度
    void init() {
        tot = 0;
        memset(head, -1, sizeof(head));
        pos = 0;
        memset(son, -1, sizeof(son));
    }
    void addedge(int u, int v) {
        edge[tot].to = v;
        edge[tot].next = head[u];
        head[u] = tot++;
    }
    void dfs(int u, int pre, int d) { //第一遍dfs求出fa,deep,num,son
        deep[u] = d;
        fa[u] = pre;
        num[u] = 1;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (v != pre) {
                dfs(v, u, d + 1);
                num[u] += num[v];
                if (son[u] == -1 || num[v] > num[son[u]])
                    son[u] = v;
            }
        }
    }
    void getpos(int u, int sp) { //第二遍dfs求出top和p
        top[u] = sp;
        p[u] = pos++;
        fp[p[u]] = u;
        if (son[u] == -1)
            return;
        getpos(son[u], sp); //先加入重边使重边在线段树中连续
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (v != son[u] && v != fa[u]) //最后加入轻边
                getpos(v, v);
        }
    }
    //线段树RMQ最值
    struct Node {
        int l, r;
        int Max;
    } tree[MAXN * 4];
    void build(int num, int l, int r) {
        tree[num].l = l;
        tree[num].r = r;
        tree[num].Max = 0;
        if (l == r)
            return;
        int mid = (l + r) / 2;
        build(num << 1, l, mid);
        build((num << 1) | 1, mid + 1, r);
    }
    void push_up(int num) {
        tree[num].Max = max(tree[num << 1].Max, tree[(num << 1) | 1].Max);
    }
    void update(int i, int k, int val) { // 更新线段树的第k个值为val
        if (tree[i].l == k && tree[i].r == k) {
            tree[i].Max = val;
            return;
        }
        int mid = (tree[i].l + tree[i].r) / 2;
        if (k <= mid)
            update(i << 1, k, val);
        else
            update((i << 1) | 1, k, val);
        push_up(i);
    }
    int query(int i, int l, int r) {
        if (l <= tree[i].l && tree[i].r <= r)
            return tree[i].Max;
        int mx1 = -INF, mx2 = -INF;
        int mid = (tree[i].l + tree[i].r) / 2;
        if (l <= mid)
            mx1 = query(i << 1, l, r);
        if (r > mid)
            mx2 = query(i << 1, l, r);
        return max(mx1, mx2);
    }
    int find(int u, int v) { //查询u->v边的最大值
        int f1 = top[u], f2 = top[v];
        int tmp = -INF;
        while (f1 != f2)  //u,v的LCA问题
        {
            if (deep[f1] < deep[f2]) {
                swap(f1, f2);
                swap(u, v);
            }
            tmp = max(tmp, query(1, p[f1], p[u]));  //u到top[u]区间最值
            u = fa[f1];
            f1 = top[u];  //迭代到top[u]的父节点
        }
        if (u == v)
            return tmp;
        if (deep[u] > deep[v])
            swap(u, v);
        return max(tmp, query(1, p[son[u]], p[v]));
    }
    int e[MAXN][3];  //树上边的始终点与边权
    int main() {
        while (T--) {
            init();
            scanf("%d", &n);
            for (int i = 0; i < n - 1; i++) {
                scanf("%d%d%d", &e[i][0], &e[i][1], &e[i][2]);
                addedge(e[i][0], e[i][1]);
                addedge(e[i][1], e[i][0]);
            }
            dfs(1, 0, 0);
            getpos(1, 1);
            build(1, 0, pos - 1);
            for (int i = 0; i < n - 1; i++) {
                if (deep[e[i][0]] > deep[e[i][1]])
                    swap(e[i][0], e[i][1]);
                update(1, p[e[i][1]], e[i][2]);
            }
            char op[10];
            int u, v;
            while (~scanf("%s", op)) {
                if (op[0] == 'D')
                    break;
                scanf("%d%d", &u, &v);
                if (op[0] == 'Q')
                    printf("%d
    ", find(u, v));
                else
                    update(1, p[e[u - 1][1]], v);
            }
        }
        return 0;
    }

     

    六,强连通分量

    1.有向图 

    HDU 4635

    int in[MAXN], out[MAXN];
    
    vector<int> G[MAXN],NG[MAXN];
    //n个点图强连通分量缩点belong[],缩点的点集NG<>
    int dfn[MAXN],low[MAXN],belong[MAXN];
    int Count,n,m,cnt;
    bool instack[MAXN];
    stack <int>stap;
    
    void init(){
        Count=cnt=0;
        memset(dfn,0,sizeof(dfn));
        memset(belong,0,sizeof(belong));
        memset(instack,0,sizeof(instack));
    }
    
    void tarjan(int x){
        int i;
        dfn[x]=low[x]=++cnt;
        stap.push(x);
        instack[x]=1;
        for(i=0;i<G[x].size();i++){
            int v=G[x][i];
            if(!dfn[v]){
                tarjan(v);
                low[x]=min(low[v],low[x]);
            }else if(instack[v])
                low[x]=min(dfn[v],low[x]);
        }
        
        if(low[x]==dfn[x]){
            Count++;
            NG[Count].clear();
            while(1){
                int tmp=stap.top();
                stap.pop();
                if (belong[x] != Count) NG[Count].push_back(tmp);
                belong[tmp]=Count;
                instack[tmp]=0;
                if(tmp==x) break;
            }
        }
    }
    
    void work(){
        init();
        for(int i=0;i<n;i++)
            if(!dfn[i]) tarjan(i);
    }
    //模板结束
    void Topsort(){
            memset(in, 0, sizeof(in));
            memset(out, 0, sizeof(out));
            for (int u = 0; u < n; u++)
                for (int i = 0; i < G[u].size(); i++){
                    int v = G[u][i];
                    if (belong[u] != belong[v]){
                        in[belong[v]] ++;
                        out[belong[u]] ++;
                    }
                }
    }
    
    int main(){
        //freopen("data.txt", "r", stdin);
        int T;
        int x, y;
        int c=1;
        scanf("%d", &T);
        while(T--){
            printf("Case %d: ",c++);
            for (int i = 0; i <= n; i++)
                G[i].clear();
            scanf("%d%d", &n, &m);
            for (int i = 0; i < m; i++){
                scanf("%d%d", &x, &y);
                x--; y--;
                G[x].push_back(y);
            }
            work();
            if(Count == 1) {printf("-1
    ");continue;}
    
            Topsort();
            int Min = 0x3fffffff;
            for (int i = 1; i <=Count; i++){
                int tmp = NG[i].size();
                if (in[i] == 0 || out[i] == 0) Min = min(Min, tmp);
            }
            x = Min, y = n - Min;
            LL ans = (LL)n * (LL)n - n - (LL)x * (LL)y - m;
            printf("%I64d
    ",ans);
        }
        return 0;
    }
    /*
    或者重新建缩点图
    for (int i = 0; i < n; i++) {
                for (int j = 0; j < G[i].size(); j++) {
                    int v = G[i][j];
                    if (belong[i] == belong[v])
                        continue;
                    NG[belong[i]].push_back(belong[v]);
                }
            }
    */

    2.无向图

    void Tarjan(int u,int father){
        int i;
        low[u] = dfn[u] = nTime ++;
        stap.push(u);
        instack[u]=true;
        int cnt=0;
        for( i = 0;i < G[u].size() ;i ++ ) {
            int v = G[u][i];
            if( ! dfn[v]) {
                Tarjan(v,u);
                low[u] = min(low[u],low[v]);
            }
            else if(father==v){  //反向边更新
                            if(cnt)low[u]=min(low[u],dfn[v]);
                           cnt++;
                    }
                    else low[u]=min(low[u],dfn[v]);
        }
        if(low[u]==dfn[u]){
            count++;
            while(1){
                int tmp=stap.top();
                stap.pop();
                belong[tmp]=count;
                instack[tmp]=false;
                if(tmp==u) break;
            }
        }
    }
    
    void work(){
        count=0;
        memset(dfn,0,sizeof(dfn));
        Tarjan(1,0);
    }

    七,拓扑排序

    对有向无环图进行拓扑排序,检测图中是否有环(注意,SPFA检测是负环)。

    ① AOV网(Activity  On Vertices)—用顶点表示活动的网络

    ② AOE网(Activity  On Edges)—用边表示活动的网络

    对有先决条件的事件找到一个完成序列。

    #define MAXN 100
    int mat[MAXN][MAXN];
    int ret[MAXN];
    int topsort(int n) {
        int d[MAXN], i, j;
        int cnt = 0;
        for (i = 0; i < n; i++) //计算每个点的入度
            for (d[i] = j = 0; j < n; d[i] += mat[j++][i])
                ;
        while (cnt < n) {
            for (i = 0; d[i] && i < n; i++)
                ;
            if (i == n)
                return 0; //说明有环
            ret[cnt++] = i;
            for (d[i] = -1, j = 0; j < n; j++) //和此点相连的入度减一
                d[j] -= mat[i][j];
        }
        return cnt;
    }
  • 相关阅读:
    [视频]K8飞刀--WinRAR远程代码执行漏洞利用视频
    [视频]K8飞刀 SQL注入点脱库演示教程
    [视频]K8飞刀 mysql注入点拿shell & UDF提权教程
    [视频]K8飞刀无代码编程之生成EXP
    [视频]K8飞刀 exploit管理功能 演示教程
    [视频]K8飞刀 Discuz! X 系列(1.5 2.5 3.x)产品升级漏洞GetShell教程
    [视频]K8飞刀 WordPress XSS添加管理员 & GetShell 教程
    [视频]K8飞刀 HackerIE自动检测网站注入教程
    [视频]K8飞刀 shellcode loader演示教程
    [视频]K8飞刀 ms15022 office漏洞演示动画
  • 原文地址:https://www.cnblogs.com/updateofsimon/p/3405377.html
Copyright © 2020-2023  润新知