蔚来杯2022牛客暑期多校训练营2 GJK

比赛链接

G

题解

知识点:思维。

\(lds(p)\) 表示最小上升子序列分划数 (Dilworth 定理)

\(lis(p)\cdot lds(p) \geq n \Rightarrow max \{lds(p), lis(p)\} \geq \lceil \sqrt n \rceil\)

因此将排列构造多个长度为 \(\lceil \sqrt n \rceil\) 递增串,并且递增串的最大值一定递减,形如 789456123 即可。

时间复杂度 \(O(n)\)

空间复杂度 \(O(1)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

bool solve() {
    int n;
    cin >> n;
    int sn = ceil(sqrt(n));
    for (int i = 1;i <= n;i += sn) {
        for (int j = min(i + sn - 1, n);j >= i;j--)
            cout << j << ' ';
    }
    cout << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

J

题解

方法一

知识点:线性回归。

显然运用线性回归公式:

\[\begin{aligned} y = Ax + B \Rightarrow \left\{ \begin{array}{l} A =& \dfrac{\sum_{i = 1}^n x_iy_i - n\bar x \bar y}{\sum_{i = 1}^n x_i^2 - n\bar x^2} \\ B =& \bar y - A\bar x \\ \end{array} \right. \end{aligned} \]

注意精度。

时间复杂度 \(O(tn)\)

空间复杂度 \(O(n)\)

方法二

知识点:三分。

如果不知道线性回归,可以运用三分斜率 \(A\) ,已知过 \((\bar x,\bar y)\)\(B\) ,同样注意精度。

时间复杂度 \(O(tn \log n)\)

空间复杂度 \(O(n)\)

代码

方法一

#include <bits/stdc++.h>
#include <cstdio>
#include <cctype>
#define ll long long

using namespace std;

namespace GTI {
    char gc(void) {
        const int S = 1 << 16;
        static char buf[S], *s = buf, *t = buf;
        if (s == t) t = buf + fread(s = buf, 1, S, stdin);
        if (s == t) return EOF;
        return *s++;
    }
    int gti(void) {
        int a = 0, b = 1, c = gc();
        for (; !isdigit(c); c = gc()) b ^= (c == '-');
        for (; isdigit(c); c = gc()) a = a * 10 + c - '0';
        return b ? a : -a;
    }
}
using GTI::gti;

int a[100007];

bool solve() {
    int n = gti();
    double xavg = (1 + n) / 2.0, yavg = 0;
    ll Au = 0, Av = 0;
    for (int i = 1;i <= n;i++) {
        a[i] = gti();
        yavg += a[i];
        Au += 1LL * i * a[i];
        Av += 1LL * i * i;
    }
    yavg /= n;
    double A = (Au - n * xavg * yavg) / (Av - n * xavg * xavg);
    double B = yavg - A * xavg;
    double ans = 0;
    for (int i = 1;i <= n;i++) ans += (A * i + B - a[i]) * (A * i + B - a[i]);
    cout << fixed << setprecision(9) << ans << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    t = gti();
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}

方法二

没写

K

题解

知识点:计数dp。

\(dp[i][j][k]\) 表示为 \(B\) 放了 \(i\) 个括号,\(B\)\(A\) 的lcs为 \(j\) ,还有 \(k\) 个未匹配的左括号。

转移方程为:

int t = j + (s[j + 1] == '(');
dp[i + 1][t][k + 1] = (dp[i][j][k] + dp[i + 1][t][k + 1]) % mod;

t = j + (s[j + 1] == ')');
if (k) dp[i + 1][t][k - 1] = (dp[i][j][k] + dp[i + 1][t][k - 1]) % mod;

上面是下一个位置放左括号的情况,下面是下一个位置放右括号的情况。

但是,通常转移方程都是当前状态是目标状态,这里是当前状态 \(dp[i][j][k]\) 是上一次状态,而目标状态并非当前状态。原因是如果知道当前状态,是找不到上一次状态的,因为不知道当前括号是不是与lcs对应的括号,因此转换递推方式,遍历已知推未知,而非遍历未知找已知推。

时间复杂度 \(O(tm^2n)\)

空间复杂度 \(O(m^2n)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int mod = 1e9 + 7;
int dp[207][207][207];

bool solve() {
    memset(dp, 0, sizeof(dp));
    int n, m;
    cin >> n >> m;
    string s;
    cin >> s;
    s = '?' + s + '?';
    dp[0][0][0] = 1;
    for (int i = 0;i <= m - 1;i++) {
        for (int j = 0;j <= min(i, n);j++) {
            for (int k = 0;k <= i;k++) {
                int t = j + (s[j + 1] == '(');
                dp[i + 1][t][k + 1] = (dp[i][j][k] + dp[i + 1][t][k + 1]) % mod;
                t = j + (s[j + 1] == ')');
                if (k) dp[i + 1][t][k - 1] = (dp[i][j][k] + dp[i + 1][t][k - 1]) % mod;
            }
        }
    }
    cout << dp[m][n][0] << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}
posted @ 2022-07-26 21:58  空白菌  阅读(53)  评论(0编辑  收藏  举报