zoukankan      html  css  js  c++  java
  • ACL Contest 1

    Conclusion

    D题用到了“对比前后变化”的思想来处理一些看起来比较复杂的东东

    A - Reachable Towns

    介绍两种做法,一种是比较好理解的并查集,一种是奇奇怪怪的(O(n))做法

    并查集做法:

    最naive的想法就是在每两个符合条件的点之间连边。但事实上确保联通性最多只要(n)条边,思考一下哪些边是必须的

    先按照横坐标排序,假设当前处理到(i),之前已经构成了一些并查集(每个并查集内的点相互联通),记录一下每个并查集内最小的值(mn_j)

    如果({y_i}>{mn_j}),就可以将(i)和集合(j)进行(merge)。可以用单调栈按照最小值维护每个并查集,只会进行(n)(merge)

    #include <bits/stdc++.h>
    using namespace std;
    void read (int &x) {
        char ch = getchar(); x = 0;
        while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 2e5 + 5;
    int n, a[N], x[N];
    int fa[N], mn[N], tp, st[N], sz[N];
    int find (int x) {
        return fa[x] == x ? x : (fa[x] = find (fa[x]));
    }
    void merge (int x, int y) {
        int fx = find (x), fy = find (y);
        if (fx != fy) {
            fa[fx] = fy, mn[fy] = min (mn[fy], mn[fx]), sz[fy] += sz[fx];
        }
    }
    signed main() {
        read (n);
        for (int i = 1, y; i <= n; ++i)
            read (x[i]), read (y), a[x[i]] = y;
        for (int i = 1; i <= n; ++i)
            fa[i] = i, mn[i] = a[i], sz[i] = 1;
        for (int i = 1; i <= n; ++i) {
            while (tp && a[i] > mn[find (st[tp])])
                merge (st[tp], i), --tp;
            st[++tp] = find (i);
        }
        for (int i = 1; i <= n; ++i)
            printf ("%d
    ", sz[find (x[i])]);
        return 0;
    }
    

    2、奇怪的(O(n))做法

    先放代码然后给证明

    #include <bits/stdc++.h>
    using namespace std;
    void read (int &x) {
        char ch = getchar(); x = 0;
        while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 2e5 + 5;
    int n, a[N], x[N], res[N], c, t[N];
    signed main() {
        read (n);
        for (int i = 1, y; i <= n; ++i)
            read (x[i]), read (y), a[x[i]] = y;
        int m = n + 1;
        for (int i = 1; i <= n; ++i) {
            m = min (m, a[i]);
            if (m == n - i + 1) t[++c] = i;
        }
        for (int i = 1; i <= c; ++i)
            for (int j = t[i]; j > t[i - 1]; --j)
                res[j] = t[i] - t[i - 1];
        for (int i = 1; i <= n; ++i) printf ("%d
    ", res[x[i]]);
        return 0;
    }
    

    意思就是先按(x)排序,找到所有(y)前缀最小值等于后面一段长度的位置,然后相邻两个位置之间的所有答案都是两个位置的差值

    先定义一个数组(q={n,n-1,n-2,...,1}),其实满足条件"前缀最小值等于后面一段长度"的位置(k)等价于:

    (y)(1—k)的子序列是(q)(1—k)的子序列的一个排列

    假设现在有了两个相邻的这样的位置(l,r),根据了上面的结论,(y)(l+1—r)的子序列就是(q)(l+1—r)的子序列的一个排列

    不难发现,这段内的所有位置无法到达(1—l)(r+1—n),所以现在只需证明(l+1—r)的所有位置可以相互到达

    引理:1、如果从位置(a)可以一步走到(b)(a<b)),那([a,b])区间内都可以相互走到

    2、如果从(a_i)可以一步走到(b_i),设(s)为所有区间([{a_i},{b_i}])的并集,则(s)中的所有位置都可以相互走到

    应该不难证明

    因为(l+1—r)内没有符合条件的位置,所以任选一个位置(win [l+1,r-1])(l+1-w)中至少有一个数在({{q_{w+1}}-{q_r}})中,(w+1—r)中至少有一个数在({{q_{l+1}}—{q_w}})

    也就是说,至少存在两个位置(ain [l+1,w],bin [w+1,r]),从(a)可以一步到(b),而所有(w)([{a_w},{b_w}])的并集一定是([l+1,r]),根据引理2,([l+1,r])中的数都可以相互走到,所以这一段的所有答案都为(r-l)

    B - Sum is Multiple

    根据求和公式,再把(/2)移过去得到:(n imes2 | k imes(k+1)),下面的(n)指的都是(n imes2)

    假设(s=k imes(k+1)),质因数分解后(n=sum{ {p_i} ^ {k_i}})(s=sum{ {p_j} ^ {k_j}})

    若要满足上述条件,则({forall}i,{p_i} ^ {k_i}|s)

    观察到(k)(k+1)互质,而要满足上述条件,则一部分的({p_i}^{k_i}|k),另一部分({p_i}^{k_i}|(k+1))

    枚举方案(a,b)使得(a imes b=n,gcd(a,b)=1)(k=x imes a, (k+1)=y imes b),即(y imes b - x imes a = 1)

    exgcd求出解,缩小到最小就ok了

    复杂度为(O({2^{num}} imes log_n))(num)(n)的约数个数,因该不会超过(20)个吧

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    const int N = 1e5 + 5;
    int n, res, p[N], c;
    int exgcd (int a, int b, int &x, int &y) {
        if (!b) { x = 1, y = 0; return a; }
        int d = exgcd (b, a % b, x, y);
        int z = x; x = y; y = z - y * (a / b);
        return d;
    }
    void update (int a, int b) {
        int x, y; exgcd (a, b, x, y);
        int k = min (x / b, y / a);
        x -= k * b, y += k * a;
        while (x <= 0) x += b, y -= a;
        while (y >= 0) y -= a, x += b;
        if (x > 0 && y < 0) res = min (res, x * a - 1);
    }
    void solve (int x, int sum, int k) {
        if (k) update (sum, n / sum);
        if (x > c) return;
        solve (x + 1, sum * p[x], 1);
        solve (x + 1, sum, 0);
    }
    signed main() {
        scanf ("%lld", &n);
        if (n == 1) return puts ("1"), 0;
        res = n - 1; n <<= 1; int t = n;
        for (int i = 2; i * i <= t; ++i) {
            if (t % i == 0) {
                p[++c] = 1;
                while (t % i == 0)
                    p[c] *= i, t /= i;
            }
        } if (t > 1) p[++c] = t;
        solve (1, 1, 1);
        return printf ("%lld
    ", res), 0;
    }
    

    C - Moving Pieces

    一个球对应一个位置,每个位置只能放一个球,有点类似二分图,用网络流求解

    连边方式很多,这里介绍边数量是(n imes m)的方法

    ((i,j))走到((x,y))的步数是(x+y-i-j),先把所有出发点的横纵坐标减去(把后面的(-i-j)算掉)

    1、因为只能向右向下走,把每个格子当作一个点,在((x,y))((x,y+1))((x+1,y))之间连边,每个格子可以走过多次,流量为无限大,价值为(0)

    2、对于每个((x,y)=)'o',从源点到((x,y))连一条边,流量为(1),价值为(0)

    3、对于每个点((x,y)),连一条从((x,y))到汇点的边,流量为(1),价值为(x+y),对应上边的式子

    跑一边费用流求出答案

    #include <bits/stdc++.h>
    using namespace std;
    void read (int &x) {
        char ch = getchar(); x = 0;
        while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 55, M = N * N * 10, K = N * N;
    int n, m, res, id[N][N]; char ch[N][N];
    int cnt = 1, h[K], to[M], nxt[M], w[M], val[M];
    void add (int u, int v, int l, int ww) {
        to[++cnt] = v, val[cnt] = ww, w[cnt] = l, nxt[cnt] = h[u], h[u] = cnt;
        to[++cnt] = u, val[cnt] = -ww, w[cnt] = 0, nxt[cnt] = h[v], h[v] = cnt;
    } int s, t, in[K], d[K], vis[K], pre[K]; queue<int> q;
    int spfa () {
        while (!q.empty()) q.pop();
        memset (d, 0xcf, sizeof (d));
        memset (vis, 0, sizeof (vis));
        q.push (s); d[s] = 0, vis[s] = 1; in[s] = 1e9;
        while (!q.empty()) {
            int u = q.front(); q.pop(); vis[u] = 0;
            // printf ("%d %d
    ", u, d[u]);
            for (int i = h[u], v; i; i = nxt[i])
                if (w[i] && d[v = to[i]] < d[u] + val[i]) {
                    d[v] = d[u] + val[i];
                    in[v] = min (in[u], w[i]), pre[v] = i;
                    if (!vis[v]) vis[v] = 1, q.push (v);
                }
        }
        return (d[t] > 0);
    }
    void update () {
        int x = t;
        while (x != s) {
            int i = pre[x];
            w[i] -= in[t], w[i ^ 1] += in[t];
            x = to[i ^ 1];
        } res += d[t];
    }
    signed main() {
        read (n), read (m);
        s = 0, t = n * m + 1;
        for (int i = 1; i <= n; ++i)
            scanf ("%s", ch[i] + 1);
         for (int i = 1; i <= n; ++i)
             for (int j = 1; j <= m; ++j)
                 id[i][j] = (i - 1) * m + j;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= m; ++j) {
                if (ch[i][j] == '#') continue;
                if (ch[i][j] == 'o')
                    res -= i + j, add (s, id[i][j], 1, 0);
                add (id[i][j], t, 1, i + j);
                if (i < n) add (id[i][j], id[i + 1][j], 1e9, 0);
                if (j < m) add (id[i][j], id[i][j + 1], 1e9, 0);
            }
        while (spfa()) update();
        return printf ("%d
    ", res), 0;
    }
    

    D - Keep Distances

    暴力做法:

    先对于每个询问区间(ql,qr),求出最大的子集大小:

    (ql)开始,每步走到最近的满足条件的数,直到超出范围,这样得出的就是最优解

    答案要求的是所有集合的并,只要对每个数分别考虑是否存在一个最大的合法集合包含这个数

    设从(ql)开始贪心得到的序列为(a),从(qr)往左走按照同样方法贪心得到的序列为(b)

    答案就是(sum{{b_i}-{a_i}+1}),意思就是在每个([{a_i},{b_i}])内的数都可能存在于集合中,其他的不行

    正确性比较好理解,(a)(b)是分别从左右端开始的最优方案,相当于是一个上下界的限制,在里面的自然都可以

    用倍增优化这个过程,以从左往右的为例,从右边开始的类似:

    (l[i][j])表示从(j)开始走了(2^i)到达的位置。上述公式里用到下标的和,再用(L[i][j])表示从(j)开始走了(2^i)过程中走过的下标的和

    预处理时间(O(nlog_n)),处理每次询问(O(log_n)),总复杂度(O((n+q)log_n))

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    void read (int &x) {
        char ch = getchar(); x = 0;
        while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 2e5 + 5;
    int n, k, a[N], l[20][N], L[20][N], r[20][N], R[20][N], c[20];
    signed main() {
        read (n), read (k); c[0] = 1;
        for (int i = 1; i < 18; ++i) c[i] = c[i - 1] << 1;
        for (int i = 1; i <= n; ++i) read (a[i]);
        for (int i = 0; i < 19; ++i) l[i][n + 1] = n + 1;
        for (int i = 1; i <= n; ++i) {
            l[0][i] = n + 1;
            int ll = i + 1, rr = n, mid;
            while (ll <= rr) {
                mid = ll + rr >> 1;
                if (a[mid] >= a[i] + k) l[0][i] = mid, rr = mid - 1;
                else ll = mid + 1;
            } L[0][i] = l[0][i];
        }
        for (int i = 1; i <= n; ++i) {
            int ll = 1, rr = i - 1, mid;
            while (ll <= rr) {
                mid = ll + rr >> 1;
                if (a[mid] <= a[i] - k) r[0][i] = mid, ll = mid + 1;
                else rr = mid - 1;
            } R[0][i] = r[0][i];
        }
        for (int i = 1; i <= 17; ++i)
            for (int j = 1; j <= n; ++j) {
                l[i][j] = l[i - 1][l[i - 1][j]];
                r[i][j] = r[i - 1][r[i - 1][j]];
                L[i][j] = L[i - 1][j] + L[i - 1][l[i - 1][j]];
                R[i][j] = R[i - 1][j] + R[i - 1][r[i - 1][j]];
            }
        int q, ql, qr; read (q);
        while (q--) {
            read (ql), read (qr);
            int res = qr - ql, now = ql;
            for (int i = 17; i >= 0; --i)
                if (l[i][now] <= qr) res -= L[i][now], now = l[i][now];
            now = qr;
            for (int i = 17; i >= 0; --i)
                if (r[i][now] >= ql) res += R[i][now] + c[i], now = r[i][now];
            printf ("%lld
    ", res + 1);
        }
        return 0;
    }
    

    E - Shuffle Window

    一开始我想的是对每个数弄。。。发现逆序对这个东西真不好搞

    跟“逆序对”关联的是每两个数的相对位置(前后关系),所以只要求出每两个数相对位置发生改变的概率

    先把原本打乱序列的过程进行转换,发现等价于:

    有一个集合(s)和一个初始为空的序列(a),初始时(s)(1—k) (k)个元素, 进行(n-k)次操作,第(i)次从(s)中随机选择一个位置放到(a)的末尾(等同于打乱),然后删除这个元素,并插入(k+i)

    有了这样的转化,就可以简单的算出对于(i<j)(i、j)相对位置改变的概率

    首先,如果两个数要改变相对位置,那么必须存在一个时刻,他们同时在集合中。设(t[i])为第(i)个位置加入集合的时间

    每次会随机从(k)个数中选出(1)个踢出集合,留下的概率是(frac{k-1}{k}),如果(i,j)要同时存在于集合中,则从(i)加入集合到(j)加入集合间的(j-i)次操作,(i)都不能被踢出集合,概率为({(frac{k-1}{k})}^{t[j]-t[i]})

    如果两个数都在集合中,易得改变的概率为(0.5)

    把上述式子的(i,j)分开,可以从(1—n)遍历(j),准便用树状数组求出(i)部分的贡献

    怎么会有这么丑的代码...

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    void read (int &x) {
        char ch = getchar(); x = 0;
        while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 2e5 + 5, mod = 998244353;
    int n, k, m, res, a[N], p[N], c[N], _c[N], __c[N], pw[N];
    int qpow (int x, int y) {
        int t = 1;
        while (y) {
            if (y & 1) t = t * x % mod;
            x = x * x % mod, y >>= 1;
        } return t;
    }
    #define plus(x, y) ((x += y) >= mod && (x -= mod))
    void add (int x, int v) {
        for (; x <= n; x += x & -x) plus (c[x], v);
    }
    int query (int x) {
        int t = 0;
        for (; x; x -= x & -x) plus (t, c[x]);
        return t;
    }
    void _add (int x, int v) {
        for (; x <= n; x += x & -x) plus (_c[x], v);
    }
    int _query (int x) {
        int t = 0;
        for (; x; x -= x & -x) plus (t, _c[x]);
        return t;
    }
    void __add (int x) {
        for (; x <= n; x += x & -x) ++__c[x];
    }
    int __query (int x) {
        int t = 0;
        for (; x; x -= x & -x) plus (t, __c[x]);
        return t;
    }
    signed main() {
        read (n), read (k);
        for (int i = 1; i <= n; ++i) read (a[i]);
        for (int i = 1; i <= k; ++i) p[i] = 0;
        for (int i = k + 1; i <= n; ++i) p[i] = i - k;
        m = (k - 1) * qpow (k, mod - 2) % mod; pw[0] = 1;
        for (int i = 1; i <= n; ++i) pw[i] = pw[i - 1] * m % mod;
        for (int i = 1; i <= n; ++i) {
            int x = _query (a[i]), y = query (n - a[i] + 1);
            res = (res + (x - y) * pw[p[i]] + __query (n - a[i] + 1)) % mod;
            int tmp = qpow (2 * pw[p[i]], mod - 2);
            _add (a[i], tmp), add (n - a[i] + 1, tmp), __add (n - a[i] + 1);
        }
        return printf ("%lld
    ", (res + mod) % mod), 0;
    }
    

    F - Center Rearranging

    这里用中文讲的很清楚了,搬运一下

    首先B由A操作而来,B一定有连续一段是没被动过的,将它们称为M;M左边都被被动过,且最后一次被用了一个push_front操作,称为L;M右边最后一次操作是push_back,称为R
    (Lcdots LMcdots MRcdots R)构成一个B的来源序列,显然只有(O(n^2))种,先枚举它,之后这个序列就确定了
    考虑B中的一种数(有3个)。A中也有3个这种数。从A,B中单独抽出这些数,考虑他们是怎么匹配的。
    先考虑B中这3个数(位置从小到大)的来源,共有10种序列,其中(LLL,RRR)显然是不可能的,剩下8种可能情况。经过艰苦卓绝的手算可以发现,除了(LMR)之外,所有M的来源位置其实是确定的,如下表

    第1组
    M** ***
    LMR LLR
    
    第2组
    **M ***
    LMR LRR
    
    ---
    
    M** **M
    MRR LLM
    
    MMM
    MMM
    
    M*M M*M
    LMM MMR
    

    确定M来源位置的用处是用于答案可行性的判定。对于所有(M),在A中原位置和B中新位置连一条边,显然这些边不能相交。(注意(M)不会被操作)
    (LMR)M的来源位置也只有两种可能,现在暂且先确定每个(LMR)M来源位置。
    对于上面的表,先考虑一个简单的情况:如果(B)只有下面3组时怎么求解最小步数,可以发现,最小步数就是3n-M的数量,这显然也是下界。因为可以直接根据B中的位置来做所有L,R操作到达B的状态。
    考虑加上1,2组之后怎么构造方案。1,2组有一个特点,就是不能乱做操作。比如第一组中的LMR,它只能是先做push_back再做push_front,也就是R的生成时间一定得比L更早。LLR也是,由于最右边的那个最后一次操作不可能是push_front,所以R的生成时间一定得比第一个L早。(当然了,第二个L的生成时间也得比第一个L早)第2组同理,L的生成时间必须比最后一个R早。
    对于第1组,在B上第3个位置向第1个位置连一条边;对于第2组,在B上第1个位置向第3个位置连一条边,代表B中一个元素必须比另一个先生成。B上还存在一些边,就是右边的L元素向左边的L连边,左边的R向右边的R连边。这些所有边构成一个拓扑图,可以感性理解不存在其它限制(雾)。如果没有环就可以按照拓扑序做操作,完美还原序列。
    怎么判环呢?可以发现,如果这个图存在环,一定可以抽出这样的环:一个第1组的第1,3位置都比一个第2组的1,3位置更小。这样的话判环就转化成了判一个二元关系。
    还有一个限制“M连的边不相交”其实也是一个二元关系。
    注意(LMR)的类型还没确定,不过只有两种情况可以用2sat求解。因为限制都是二元关系所以就很好做了。

    #include <bits/stdc++.h>
    using namespace std;
    void read (int &x) {
        char ch = getchar(); x = 0;
        while (!isdigit(ch)) ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
    } const int N = 105;
    int n, m, kind[N], dfn[N], bl[N], low[N], num, tp, st[N], in[N], tot;
    #define pb push_back
    vector<int> posa[N], posb[N], to[N]; vector<pair<int, int> > match;
    #define fi first
    #define se second
    void tarjan (int u) {
        dfn[u] = low[u] = ++num;
        st[++tp] = u; in[u] = 1;
        for (auto v : to[u])
            if (!dfn[v]) 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]) {
            ++tot; int t;
            do t = st[tp--], bl[t] = tot, in[t] = 0; while (t != u);
        }
    }
    int judge (int l, int r) {
        match.clear(); memset (kind, 0, sizeof (kind));
        #define get(p) (p <= l ? "L" : p >= r ? "R" : "M")
        for (int i = 1; i <= n; ++i) {
            string str = (string)get (posb[i][0]) + get (posb[i][1]) + get (posb[i][2]);
            if (str == "LLL" || str == "RRR") return 0;
            if (str == "LLR") kind[i] = 1; if (str == "LRR") kind[i] = 2; if (str == "LMR") kind[i] = 3;
            if (str == "MMR") match.pb ({posa[i][0], posb[i][0]}), match.pb ({posa[i][2], posb[i][1]});
    		if (str == "LMM") match.pb ({posa[i][0], posb[i][1]}), match.pb ({posa[i][2], posb[i][2]});
    		if (str == "MMM") for (int j = 0; j < 3; ++j) match.pb ({posa[i][j], posb[i][j]});
    		if (str == "LLM") match.pb ({posa[i][2], posb[i][2]});
    		if (str == "MRR") match.pb ({posa[i][0], posb[i][0]});
        }
    
        sort (match.begin(), match.end());
        #define check(a, b) (posb[a][0] > posb[b][0] && posb[a][2] > posb[b][2])
        for (int i = 1; i < match.size(); ++i)
            if (match[i].se < match[i - 1].se) return 0;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                if (kind[i] == 1 && kind[j] == 2 && check (i, j)) return 0;
        for (int i = 1; i <= n * 2; ++i) to[i].clear();
        #define cross(a, b, c, d) ((a < c) ^ (b < d))
        // if (l == 2 && r == 8) puts ("666");
        for (int i = 1; i <= n; ++i) {
            if (kind[i] != 3) continue; int tmp = 0;
            for (auto j : match) {                  //根据 M 交叉限制条件
            	if (cross (j.fi, j.se, posa[i][0], posb[i][1])) tmp |= 2;
            	if (cross (j.fi, j.se, posa[i][2], posb[i][1])) tmp |= 1;
            }
            for (int j = 1; j <= n; ++j)
            	if (kind[j] == 1) { if (check (j, i)) tmp |= 1; }
            	else if (kind[j] == 2) { if (check (i, j)) tmp |= 2; }
            	else {
            		if (check (i, j) || cross (posa[i][0], posb[i][1], posa[j][2], posb[j][1])) to[i].pb (j);
            		if (check (j, i) || cross (posa[i][2], posb[i][1], posa[j][0], posb[j][1])) to[i + n].pb (j + n);
            		if (cross (posa[i][0], posb[i][1], posa[j][0], posb[j][1])) to[i].pb (j + n);
            		if (cross (posa[i][2], posb[i][1], posa[j][2], posb[j][1])) to[i + n].pb (j);
            	}
            if (tmp == 3) return 0;
            if (tmp & 1) to[i + n].push_back (i);
            if (tmp & 2) to[i].push_back (i + n);
        }
        memset (dfn, 0, sizeof (dfn));
        memset (bl, 0, sizeof (bl));
        memset (in, 0, sizeof (in));
        memset (low, 0, sizeof (low));
        num = tp = tot = 0;
        for (int i = 1; i <= n * 2; ++i) if (!dfn[i]) tarjan (i);
        for (int i = 1; i <= n; ++i) if (bl[i] == bl[n + i]) return 0; return 1;
    }
    signed main() {
        read (n), m = n * 3; int res = -1;
        for (int i = 1, x; i <= m; ++i) read (x), posa[x].pb (i);
        for (int i = 1, x; i <= m; ++i) read (x), posb[x].pb (i);
        for (int l = 0; l <= m; ++l)
            for (int r = l + 1; r <= m + 1; ++r)
                if (judge (l, r)) res = max (res, r - l - 1);
        return printf ("%d
    ", res > -1 ? m - res : -1), 0;
    }
    
  • 相关阅读:
    Activiti系列——如何在eclipse中安装 Activiti Designer插件
    C语言 二维数组与指针笔记
    Ubuntu linux设置从当前目录下加载动态库so文件
    Ubuntu14.04安装nfs服务器
    Ubuntu14.04 搭建FTP服务器
    Linux备忘命令
    Java实现对xml文件的增删改查
    Java利用jacob实现打印Excel文件
    git操作列表
    swiper 窗口宽度变化,页面宽度高度变化 导致自动滑动 解决方案
  • 原文地址:https://www.cnblogs.com/whx666/p/13717748.html
Copyright © 2011-2022 走看看