W
H
X

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\n", 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\n", 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\)内没有符合条件的位置,所以任选一个位置\(w\in [l+1,r-1]\)\(l+1-w\)中至少有一个数在\({{q_{w+1}}-{q_r}}\)中,\(w+1—r\)中至少有一个数在\({{q_{l+1}}—{q_w}}\)

也就是说,至少存在两个位置\(a\in [l+1,w],b\in [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\times2 | k\times(k+1)\),下面的\(n\)指的都是\(n\times2\)

假设\(s=k\times(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\times b=n,gcd(a,b)=1\)\(k=x\times a, (k+1)=y\times b\),即\(y\times b - x\times a = 1\)

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

复杂度为\(O({2^{num}}\times 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\n", res), 0;
}

C - Moving Pieces

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

连边方式很多,这里介绍边数量是\(n\times 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\n", 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\n", 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\n", 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\n", (res + mod) % mod), 0;
}

F - Center Rearranging

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

首先B由A操作而来,B一定有连续一段是没被动过的,将它们称为M;M左边都被被动过,且最后一次被用了一个push_front操作,称为L;M右边最后一次操作是push_back,称为R
\(L\cdots LM\cdots MR\cdots 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\n", res > -1 ? m - res : -1), 0;
}
posted @ 2020-09-23 20:51  -敲键盘的猫-  阅读(339)  评论(0)    收藏  举报