• Day6 && Day7图论


    并查集

    A-How Many Answers Are Wrong

    题意:已知区间[1,n],给出m组数据,即[l,r]区间内数据之和为s,求错误数据的数量。
    思路:

    • 并查集中路径压缩的过程中如何更新关系域是关键
    • 根据数据定义一些合理的关系(如:集合中的根与集合中的元素的关系),才能对集合进行合并。这里可能会分一些情况讨论,最终应该是可以化简的
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    const int maxn = 200020;
    int f[maxn];
    int sum[maxn];   //记录当前结点到根的距离
    int find(int x){
    //return x==f[x]?x:find(f[x]);
        if(x != f[x]){
            int roota = f[x];
            f[x] = find(f[x]);
            sum[x] += sum[roota];
            //x->rootb = x->roota + roota->rootb
        }
        return f[x];
    }
    int main(){
        //freopen("in.txt","r",stdin);
        int n,m;
        while(scanf("%d%d", &n, &m) != EOF){
            for(int i=0; i<=n; i++){
                f[i] = i;
                sum[i] = 0;
            }
            int ans = 0;
            while(m--){
                int a, b, v;
                scanf("%d%d%d", &a, &b, &v);
                a--;
                //join
                int roota = find(a);
                int rootb = find(b);
                if(roota == rootb){
                    if(sum[a]-sum[b] != v)    ans++;
                }else{
                    f[roota] = rootb;
                    //定义大的数字是小的数字的根
                    sum[roota] = -sum[a] + sum[b] + v;
                    //变换成等式 sum[roota] + sum[a] = sum[b] + v;
                    //a->roota + roota->rootb == b->rootb + a->b
                }
                //join
            }
            printf("%d\n",ans);
        }
        return 0;
    }
    

    B - 食物链

    对于集合里的任意两个元素x,y而言,它们之间必定存在着某种联系,因为并查集中的元素均是有联系的(这点是并查集的实质),否则也不会被合并到当前集合中。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 50010;
    int f[maxn], relation[maxn];
    //relation[i]:i到根的偏移量
    int sum;
    void init(int n){
        for(int i=0; i<n; i++){
            f[i] = i;
            relation[i] = 0;
        }
        sum = 0;
    }
    int find(int x){
        if(x != f[x]){
            int rootx = f[x];
            f[x] = find(rootx);
            //路径压缩更新关系域//这是关键1
            relation[x] = (relation[x] + relation[rootx]) % 3;
        }
        return f[x];
    }
    void join(int x, int y, int d){
        int rootx = find(x);
        int rooty = find(y);
        if(rootx != rooty){
            f[rooty] = rootx;
            relation[rooty] = (3 + d-1 + relation[x] - relation[y]) % 3;
            //此处d-1题中给出的询问已知条件即0同类,1吃,2被吃
            /*先用等式表达:
            这里并非简单的加减,实质应该是关系的转移,可以用向量理解
                 r->rx + x->y = y->ry + ry->rx
            ====>r[x] + k = r[y] + ry->rx
            ====>ry->rx = r[x] + k - r[y]
               k = d - 1
               同理下面两式也得
            */
        }else{
            if((d==1) && (relation[x] != relation[y])){
                sum++;
            }else if((d==2) && ((d-1) != (relation[y] - relation[x] + 3) % 3)){
                sum++;
            }
        }
    }
    int main(){
        //freopen("in.txt","r",stdin);
        int n, k;
        scanf("%d%d", &n, &k);
        init(n);
        for(int i=0; i<k; i++){
            int d, x, y;
            scanf("%d%d%d",&d, &x, &y);
            if(x > n || y > n){
                sum++;
            }else if((d==2) && (x == y)){
                sum++;
            }else{
                join(x, y, d);
            }
        }
        printf("%d\n",sum);
        return 0;
    }
    

    C - A Bug's Life

    题意:给定n个bugs,编号依次1、2、……n。它们之间只有雄性和雌性之分,并给定m对交配的情况,根据这m对交配的情况,判断是否有同性恋出现的情况。

    Thinking:

    • 定义relation[i]:结点i到根的距离:即相对与根节点的性别。
    • x,y在同一集合,他们相对根的距离即相对根的性别,而他们又同根,易得x,y的相对性别
    • x,y不再同一集合,思考向量得关系: y->x + x->rootx = y->rooty + rooty->rootx 其中y->x应为1。
    #include <cstdio>
    const int maxn = 2010;
    int f[maxn];
    int relation[maxn];
    bool flag;
    void init(int n){
        //这里n没有初始完( i < n )导致WA了半天
        for(int i=0; i<=n; i++){
            f[i] = i;
            relation[i] = 0;
        }
    }
    int find(int x){
        if(x != f[x]){
            int rootx = f[x];
            f[x] = find(rootx);
            relation[x] = (relation[rootx] + relation[x]) % 2;
        }
        return f[x];
    }
    void join(int x, int y){
        int rootx = find(x);
        int rooty = find(y);
        if(rootx == rooty){
            if(relation[x] == relation[y]){
                flag = true;
            }
        }else{
            /*A了这题后在网上看到很多解法中这里对x,y进行分类,
            即 x<y  :
                    f[rooty] = rootx;
                    relation[rooty] = ((relation[x]+1) + relation[y]) % 2;
              x>y    :
                       f[rootx] = rooty;
                    relation[rootx] = ((relation[y]+1)%2 + relation[x]) % 2;
                这里我认为应该可以不需要,因为查找过程中进行了路径压缩
            */
            f[rooty] = rootx;
            relation[rooty] = ((relation[x]+1) - relation[y]) % 2;
                //这里+由向量y->x + x->rootx = y->rooty + rooty->rootx得应该是-,但%2好像算术上是等价的
                //这里我个人认为是减,当然%2时加和减结果一样, 这里留待思考?????
        }
    }
    int main(){
        //freopen("in.txt","r",stdin);
        int T;
        scanf("%d", &T);
        for(int t=1; t<=T; t++){
            int b, inter;
            scanf("%d%d",&b, &inter);
            init(b);
            flag = false;
            for(int i=0; i<inter; i++){
                int x, y;
                scanf("%d%d", &x, &y);
                if(flag){
                    continue;
                }else{
                    join(x, y);
                }
            }
            printf("Scenario #%d:\n", t);
            if(flag){
                printf("Suspicious bugs found!\n\n");
            }else{
                printf("No suspicious bugs found!\n\n");
            }
        }
        return 0;
    }
    

    无向图的割点

    E - SPF

    题意:一个网络中割点的个数,和割点可以将网络分成几部分。

    Thinking:

    • 不用belong记录强连通分量集合的编号,需要加入parent[]记录父节点,child判断子树数量。这是为了分开计算割点是根 和非根的情况。
    • 割点的判断:number[u]<=low[v]即v必须通过u才能访问u的祖先,这里u就是割点。
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 1005;
    struct Node{
        int to;
        int next;
    }edge[maxn*maxn];
    int head[maxn], E, V;
    void add_edge(int u, int v){
        edge[E].to = v;    edge[E].next = head[u];    head[u] = E++;
        edge[E].to = u;    edge[E].next = head[v];    head[v] = E++;
        V = max(max(u,v), V);
    }
    int size_[maxn];
    // 记录删除i结点后的连通分量个数
    int low[maxn], number[maxn], parent[maxn];
    ////Let LOWPT(v) be the smallest vertex in the set ${v}\bigcap { w| v \xrightarrow{*} -\rightarrow w}$
    ///number[u]为结点u搜索的次序编号
    ///parent[i]:记录i的父亲结点
    bool flag; ///是否有割点存在
    int cnt;
    
    void dfs(int u){
        int child = 0;
        //记录子树的数量
        low[u] = number[u] = ++cnt;
        for(int i=head[u]; i!=-1; i=edge[i].next){
            int v = edge[i].to;
            if(!number[v]){
                child++;
                parent[v] = u;
                dfs(v);
                ///dfs(v)更新low[v]
                low[u] = min(low[u], low[v]);
                //必须通过u,v才能访问u的祖先number[u]<=low[v]
                if((parent[u]==-1 && child>=2) || (parent[u]!=-1 && number[u]<=low[v])){
                    /*
                    child>2保证如果是根节点,将比非根节点少加1
                    因为如果割点不是根节点,其连通分量要加上其父辈
                    */
                    flag = true;
                    size_[u]++;
                }
            }else{
                low[u] = min(low[u], number[v]);
            }
        }
    }
    void init(){
        memset(size_, 0, sizeof(size_));
        memset(number, 0, sizeof(number));
        memset(low, 0, sizeof(low));
        memset(head, -1, sizeof(head));
        memset(parent, -1, sizeof(parent));
        E = V = 0;
        flag = false;
        cnt = 0;
    }
    int main(){
    //    freopen("in.txt","r", stdin);
        int cas;
        for(int tt=1; ;tt++){
            init();
            //input
            int u, v=-1;
            while(scanf("%d", &u) && u){
                scanf("%d", &v);
                add_edge(u, v);
            }
            if(v == -1)    break;
            //input
            dfs(V);
            printf("Network #%d\n",tt);
            if(flag){
                for(int i=1; i <= V; i++){
                    if(size_[i] > 0){
                        printf("  SPF node %d leaves %d subnets\n",i,size_[i]+1);
                    }
                }
            }else{
                printf("  No SPF nodes\n");
            }
            printf("\n");
        }
        return 0;
    }
    

    Thinking2:

    • pre[v]<pre[u]&&v!=f 理解:在无向图中edge(u, f)不是反向边(第一次处理时从后代指向祖先的边),只是树边edge(f, u)的第二次访问。
    • if(lowv > pre[u]) { u->v is bridge} 这是对桥的判断。
    • 在无向连通图G的dfs树中,非根结点u是G的割点当且仅当u存在一个子节点v,使得v及其所有后代都没有反向边连回u的祖先(连回u不算)。
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #include <stack>
    using namespace std;
    
    const int maxn = 1010;
    vector<int> G[maxn];
    
    int iscut[maxn];
    
    int low[maxn], pre[maxn];
    //pre:时间戳
    //low[u]:u及其后代所能连回的最早祖先的pre值
    int cnt, V;
    
    int dfs(int u, int f){
        int lowu = pre[u] = ++cnt;
        int child = 0;   //子节点数目
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i];
            if(!pre[v]){   //未访问v
                child++;
                int lowv = dfs(v, u);
                lowu = min(lowv, lowu);   //用后代low更新low u
                if(lowv >= pre[u]){
                    iscut[u]++;
                }
            }else if(pre[v]<pre[u] && v!=f){
            //v==f无向图,此边不属于反向边,属于树边的二次访问
                lowu = min(lowu, pre[v]);  //反向边更新u
            }
        }
        if(f<0 && child==1){ //根节点特判
            iscut[u] = 0;
        }
        return low[u] = lowu;
    }
    void add_edge(int a, int b){
        G[a].push_back(b); G[b].push_back(a);
        V = max(max(a, b), V);
    }
    void init(){
        memset(pre, 0, sizeof(pre));
        memset(iscut, 0, sizeof(iscut));
        for(int i=0; i<maxn; i++){  G[i].clear();  }
        cnt = 0;  V = 0;
    }
    int main(){
        //freopen("in.txt", "r", stdin);
        for(int tt=1; ;tt++){
            init();
            int u, v=-1;
            while(scanf("%d", &u) && u){
                scanf("%d", &v);
                add_edge(u, v);
            }
            if(v == -1) break;
            dfs(V, -1);
            printf("Network #%d\n", tt);
            bool flag = true;
            for(int i=0; i<=V; i++){
                if(iscut[i]){
                    printf("  SPF node %d leaves %d subnets\n", i, iscut[i]+1);
                    flag = false;
                }
            }
            if(flag){
                printf("  No SPF nodes\n");
            }puts("");
        }
        return 0;
    }
    

    有向图的强连通分量

    F - Proving Equivalences

    题意:给一个有向图,试求添加多少条边可以使该图成为强连通图。

    Thinking:

    • 要利用DAG的性质,则需要用Tarjan算法缩点,将有向图转为DAG。
    • 至于添加多少条边,可以这样想,怎样操作能构成最大环,即将DAG头尾相连即可。即求入度和出度的max();
    • 需要特判强连通分量个数为一的情况如(1,2)(2,1),则不需要加边。
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <stack>
    using namespace std;
    const int maxn = 20010;
    struct Node{
        int to;
        int next;
    }edge[maxn * 3];
    int head[maxn],cnt;
    stack<int> S;
    int low[maxn], number[maxn];
    int belong[maxn], scc;
    //保存每个点属于的强连通集合的编号;  强连通分量编号最大值(即个数
    void add_edge(int u, int v){
        edge[++cnt].to = v;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    void dfs(int u){
        S.push(u);
        number[u] = low[u] = ++cnt;
        for(int i = head[u]; i!=-1; i = edge[i].next){
            int v = edge[i].to;
            if(!number[v]){
                dfs(v);
                low[u] = min(low[u], low[v]);
            }else if(!belong[v]){
                low[u] = min(low[u], number[v]);
            }
        }
        if(number[u] == low[u]){
            scc++;
            int v;
            do{
                v = S.top(), S.pop();
                belong[v] = scc;
            }while(u != v);
        }
    }
    int in[maxn], out[maxn];
    void init(){
        cnt = scc = 0;
        memset(head, -1, sizeof(head));
        memset(number, 0, sizeof(number));
        memset(low, 0, sizeof(low));
        memset(in, 0, sizeof(in));
        memset(out, 0, sizeof(out));
        memset(belong, 0, sizeof(belong));
        while(!S.empty())    S.pop();
    }
    int main(){
        //freopen("in.txt", "r", stdin);
        int T;
        scanf("%d", &T);
        while(T--){
            init();
            int n, m;
            scanf("%d%d", &n, &m);
            for(int i=0; i<m; i++){
                int u, v;
                scanf("%d%d", &u, &v);
                add_edge(u, v);
            }
            cnt = 0;
            for(int i=1; i<=n; i++){
                if(!number[i]){
                    dfs(i);
                }
            }
            if(scc == 1){
                printf("0\n");
                continue;
            }
            for(int i=1; i<=n; i++){
                for(int j=head[i]; j!=-1; j=edge[j].next){
                    int v = edge[j].to;
                    if(belong[v] != belong[i]){
                        in[belong[v]]++;
                        out[belong[i]]++;
                    }
                }
            }
            int ans1 = 0, ans2 = 0;
            for(int i=1; i<=scc; i++){
                if(!out[i])    ans1++;
                if(!in[i])    ans2++;
            }
            printf("%d\n",max(ans1, ans2));
        }
        return 0;
    }
    
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 20010;
    vector<int> G[maxn];
    stack<int> S;
    int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
    //sccno[i]: i所在的SCC编号
    void dfs(int u){
        pre[u] = lowlink[u] = ++dfs_clock;
        S.push(u);
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i];
            if(!pre[v]){
                dfs(v);
                lowlink[u] = min(lowlink[u], lowlink[v]);
            }else if(!sccno[v]){
                lowlink[u] = min(lowlink[u], pre[v]);
            }
        }
        if(lowlink[u] == pre[u]){
            scc_cnt++;
            while(true){
                int x = S.top();  S.pop();
                sccno[x] = scc_cnt;
                if(x == u) break;
            }
        }
    }
    int find_scc(int n){
        dfs_clock = scc_cnt = 0;
        memset(sccno, 0, sizeof(sccno));
        memset(pre, 0, sizeof(pre));
        for(int i=0; i<n; i++){
            if(!pre[i]) dfs(i);
        }
    }
    int in[maxn], out[maxn];
    
    int main(){
        //freopen("in.txt", "r", stdin);
        int t; scanf("%d", &t);
        while(t--){
            int n, m;  scanf("%d%d", &n, &m);
            for(int i=0; i<n; i++) G[i].clear();
            for(int i=0; i<m; i++){
                int a, b; scanf("%d%d", &a, &b); a--, b--;
                G[a].push_back(b);
            }
            find_scc(n);
            // 对缩点后的DAG入度和出度计算
            //in[i]:i的入度为0
            for(int i=1; i<=scc_cnt; i++) in[i] = out[i] = 1;
            for(int u=0; u<n; u++){
                for(int i=0; i<G[u].size(); i++){
                    int v = G[u][i];
                    if(sccno[u] != sccno[v]) in[sccno[v]] = out[sccno[u]] = 0;
                    //u,v对应的出入度不为0
                }
            }
            //
            int a = 0, b = 0;
            for(int i=1; i<=scc_cnt; i++){
                if(in[i])  a++;
                if(out[i]) b++;
            }
            int ans = max(a, b);
            if(scc_cnt == 1) ans = 0;
            printf("%d\n", ans);
        }
        return 0;
    }
    

    G - The Largest Clique

    题意:给一个有向图G,求一个结点数最大的结点集(任意两点相互可达)。

    Targan缩点 + DAG上的最长路。 给一个含圈的有向图,求最长路。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1010;
    vector<int> G[maxn], mp[maxn];
    stack<int> S;
    int pre[maxn], lowlink[maxn], sccno[maxn], dfs_clock, scc_cnt;
    //sccno[i]: i所在的SCC编号
    void dfs(int u){
        pre[u] = lowlink[u] = ++dfs_clock;
        S.push(u);
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i];
            if(!pre[v]){
                dfs(v);
                lowlink[u] = min(lowlink[u], lowlink[v]);
            }else if(!sccno[v]){
                lowlink[u] = min(lowlink[u], pre[v]);
            }
        }
        if(lowlink[u] == pre[u]){
            scc_cnt++;
            int x;
            do{
                x = S.top();  S.pop();
                sccno[x] = scc_cnt;
            }while(x != u);
        }
    }
    void find_scc(int n){
        dfs_clock = scc_cnt = 0;
        memset(sccno, 0, sizeof(sccno));
        memset(pre, 0, sizeof(pre));
        for(int i=0; i<n; i++){
            if(!pre[i]) dfs(i);
        }
    }
    int size_[maxn], d[maxn];
    
    //DAG上的最长距离
    int dp(int u) {
        if(d[u] >= 0) return d[u];
        d[u] = size_[u];
        for(int i = 0; i < mp[u].size(); i++){
            int v = mp[u][i];
            d[u] = max(d[u], dp(v) + size_[u]);
        }
        return d[u];
    }
    void build(int n){
        for(int i=0; i<=scc_cnt; i++) mp[i].clear();
        memset(size_, 0, sizeof(size_));
        for(int i=0; i<n; i++) size_[sccno[i]]++;
        for(int i=0; i<n; i++){
            for(int j=0; j<G[i].size(); j++){
                int v = G[i][j];
                if(sccno[i] != sccno[v]){
                    mp[sccno[i]].push_back(sccno[v]);
                }
            }
        }
    }
    int main(){
    //    freopen("in.txt", "r", stdin);
        int t; scanf("%d", &t);
        while(t--){
            int n, m;  scanf("%d%d", &n, &m);
            for(int i=0; i<n; i++) G[i].clear();
            for(int i=0; i<m; i++){
                int a, b; scanf("%d%d", &a, &b); a--; b--;
                G[a].push_back(b);
            }
    
            find_scc(n);
    
            build(n);
    
            int ans = 0;
            memset(d, -1, sizeof(d));
            for(int i=1; i<=scc_cnt; i++){
                ans = max(ans, dp(i));
            }
            printf("%d\n", ans);
        }
        return 0;
    }
    

    无向图的双连通分量

    K - Knights of the Round Table(uva 3523)

    题意:n个骑士举行会议,每次圆桌会议至少三人,且骑士数目为奇数,相互憎恨的骑士不能再相邻位置,问多少个骑士不能参加会议。

    二分图 + BCC

    建图:两个骑士可以相邻,则建一条无向边。

    求不在任何一个简奇圈上的结点个数。

    简单圈上的所有结点必然属于同一个双连通分量;二分图没有奇圈。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1010;
    
    struct edge{
        int u, v;
        edge(int a, int b) : u(a), v(b) {}
    };
    int pre[maxn], iscut[maxn], bccno[maxn], dfs_clock, bcc_cnt;
    //bccno[i]=x:第i个顶点属于第x个点-双连通分量
    vector<int> G[maxn], bcc[maxn];
    //bcc[i]: 编号为i的点-双连通分量的所有结点
    stack<edge> S;
    
    int dfs(int u, int f){
        int lowu = pre[u] = ++dfs_clock;
        int child = 0;
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i];
            edge e = (edge){u, v};
            if(!pre[v]){
                S.push(e);
                child++;
                int lowv = dfs(v, u);
                if(lowv >= pre[u]){
                    iscut[u]++;
                    bcc_cnt++; bcc[bcc_cnt].clear();
                    while(true){
                        edge x = S.top(); S.pop();
                        if(bccno[x.u] != bcc_cnt){
                            bcc[bcc_cnt].push_back(x.u);
                            bccno[x.u] = bcc_cnt;
                        }
                        if(bccno[x.v] != bcc_cnt){
                            bcc[bcc_cnt].push_back(x.v);
                            bccno[x.v] = bcc_cnt;
                        }
                        if(x.u == u && x.v == v){
                            break;
                        }
                    }
                }
            }else if(pre[v] < pre[u] && v != f){
                S.push(e);
                lowu = min(lowu, pre[v]);
            }
        }
        if(f < 0 && child==1) iscut[u]=0;
        return lowu;
    }
    
    void find_bcc(int n){
        memset(pre, 0, sizeof(pre));
        memset(iscut, 0, sizeof(iscut));
        memset(bccno, 0,  sizeof(bccno));
        dfs_clock = bcc_cnt = 0;
        for(int i=0; i<n; i++){
            if(!pre[i]) dfs(i, -1);
        }
    }
    int color[maxn];
    bool odd[maxn];
    bool bipartite(int u, int b){
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i];
            if(bccno[v] != b) continue;
            if(color[v] == color[u]){
                return false;
            }
            if(!color[v]){
                color[v] = 3 - color[u];
                if(!bipartite(v, b)) return false;
            }
        }
        return true;
    }
    int A[maxn][maxn];
    
    int main(){
        //freopen("in.txt", "r", stdin);
        int kase = 0, n, m;
        while(scanf("%d%d", &n, &m) != EOF && n){
            for(int i=0;i<n;i++)G[i].clear();
            memset(A, 0, sizeof(A));
    
            for(int i=0;i<m;i++){
                int a,b; scanf("%d%d",&a, &b); a--,b--;
                A[a][b] = A[b][a] = 1;
            }
            for(int i=0; i<n; i++){
                for(int j=i+1; j<n; j++){
                    if(!A[i][j]) G[i].push_back(j), G[j].push_back(i);
                }
            }
    
            find_bcc(n);
    
            memset(odd, 0, sizeof(odd));
            for(int i=1; i<=bcc_cnt; i++){
                for(int j=0; j<bcc[i].size(); j++){  //同一个BBC内统一编号
                    bccno[bcc[i][j]] = i;
                }
                int u = bcc[i][0];
    
                memset(color, 0, sizeof(color));
                color[u] = 1;
                if(!bipartite(u, i)){   //不是二分图,标记为奇圈
                    for(int j=0; j<bcc[i].size(); j++){
                        odd[bcc[i][j]] = true;
                    }
                }
            }
            int ans = n;
            for(int i=0; i<n; i++) if(odd[i]) ans--;
            printf("%d\n", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    shell 遍历当前目录以及所有子目录下文件
    linux shell 字符串操作(长度,查找,替换)详解
    将自定义结构存入std::set 或者 std::map
    CentOS下搭建SVN服务器
    基于Debian的linux系统软件安装命令
    rm搭配grep删除符合条件的文件
    Centos系统环境
    怎么恢复用mysqldump备份数据和恢复数据
    mac 安装 node.js 的 canvas
    centos 6.5 安装 node.js 的 canvas模块
  • 原文地址:https://www.cnblogs.com/seaupnice/p/9440137.html
Copyright © 2020-2023  润新知