目录

多校冲刺 NOIP 20211029 模拟 (18)

Task 1 莓良心

:joy:

确实没良心,干了1个半小时,想到八该一反对里的反对盲目蛮干就弃了。

若 $u, v$ 被分在同一组中,则对答案有 $w_u + w_v$ 的贡献($u = v$ 也算)。那么就有

$$ \begin{array}{l} \text { ans }=\left(\sum w_{i}\right) \cdot\left{\begin{array}{l} n \\\
k \end{array}\right}+\sum_{u \neq v}\left(w_{u}+w_{v}\right) \cdot\left{\begin{array}{c} n-1 \\\
k \end{array}\right} \\\
a n s=\left(\sum w_{i}\right) \cdot\left(\left{\begin{array}{l} n \\\
k \end{array}\right}+(n-1)\left{\begin{array}{c} n-1 \\\
k \end{array}\right}\right) \end{array} $$

其中 $\left {\begin{array}{l}n\\\ k\end{array} \right}$ 是第二类斯特林数,即将 $n$ 个数分为 $k$ 个非空集合的方案数。 若按照递推式计算,时间复杂度 $O(n^2)$ ,期望得分 40 分。 考虑经典的做法,利用容斥原理计算第二类斯特林数,

$$

\left{\begin{array}{l} n \\\
k \end{array}\right}=\frac{1}{k !} \sum_{i=0}^{k}(-1)^{i}\left(\begin{array}{l} k \\\
i \end{array}\right)(k-i)^{n}

$$

直接用快速幂计算每个 $(k − i)^n$ ,时间复杂度 $O(n log n)$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <bits/stdc++.h>
// 这题真没良心
#define For(i, l, r) for (int i = (l), i##end = (r); i <= i##end; ++i)
#define Fordown(i, r, l) for (int i = (r), i##end = (l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
const int maxn = 1000010;
const int mod = 998244353;

#define int int64_t

template <typename NBXIN>
NBXIN read() {
  NBXIN s = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    s = s * 10 + ch - '0';
    ch = getchar();
  }
  return s * f;
}

int a[maxn], fac[maxn], inv[maxn];
int qpow(int base, int pwr) {
  int ans = 1;
  while (pwr) {
    if (pwr & 1) {
      ans = 1ll * base * ans % mod;
    }
    base = 1ll * base * base % mod;
    pwr >>= 1;
  }
  return ans;
}

int C(int n, int m) { return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod; }

int st(int n, int k) {
  int tmp = 0;
  For(i, 0, k) {
    if (i & 1) {
      tmp -= 1ll * C(k, i) * qpow(k - i, n) % mod;
    } else {
      tmp += 1ll * C(k, i) * qpow(k - i, n) % mod;
    }
    tmp = (tmp + mod) % mod;
  }
  return 1ll * qpow(fac[k], mod - 2) * (tmp + mod) % mod;
}

int32_t main() {
  freopen("ichigo.in", "r", stdin);
  freopen("ichigo.out", "w", stdout);
  int n, m;
  n = read<int>();
  m = read<int>();
  int swi = 0;
  For(i, 1, n) {
    a[i] = read<int>();
    swi = (swi + a[i]) % mod;
  }
  fac[0] = 1;
  For(i, 1, n) { fac[i] = fac[i - 1] * i % mod; }
  inv[0] = 1;
  inv[n] = qpow(fac[n], mod - 2);
  Fordown(i, n - 1, 1) { inv[i] = 1ll * (i + 1) * inv[i + 1] % mod; }
  int ans = 0;
  ans = (ans + 1ll * swi * ((n - 1) * st(n - 1, m) % mod + st(n, m) % mod) % mod) %
        mod;
  cout << ans << endl;
  return 0;
}

Task 2 尽梨了

先想一个贪心,先把a为0的都选上,然后看情况插入a不为0的。发现这样会假,就去考虑迪屁。

考虑如果在时刻 $t$ 在商店 $i$ 购买物品,结束后立即去商店 $j$ 购买物品。 那么 $j$ 会因为在 $i$ 处等候而额外花费 $(a_i \cdot t + b_i + 1) \cdot aj$ 的时间。 如果我们将二者交换顺序,在时刻 $t$ 在 $j$ 购买,结束后立即去 $i$ 购买,$i$ 会额 外花费 $(a_j \cdot t + b_j + 1) \cdot a_i$ 的时间。 若先去 $i$ 比先去 $j$ 更优, 就需要满足 $(a_i \cdot t + b_i + 1) \cdot a_j \leq (a_j \cdot t + b_j + 1) \cdot a_i$ 即 $(b_i + 1) \cdot a_j \leq (b_j + 1) \cdot a_i$

可以发现 $i$ 是否比 $j$ 优与当前时刻 $t$ 无关。于是可以先对所有商店排序,得到序列 $p$ ,那么我们实际去的商店按照时间先后形成的序列一定是 $p$ 的一个子序列。

所以可以写DP,设 $dp[i][j]$ 表示考虑了序列 $p$ 中的前 $i$ 家商店,已经买了 $j$ 件物品花费的最小时间,转移就考虑选不选第 $i$ 家商店就行, $O(n^2)$

然后,你快乐的码完,快乐的提交,然后发现你快乐的T了。因为对于a不等于0的商店来说,每选一个,花费是成指数增长的,所以最多只能去 $logT$ 个商店。然后第二维只开到 $logT$ 就行了。然后你多了20分。

再回到我一开始说的那个贪心策略,把它运用到现在的DP上。只处理到最后一个非0的位置(因为a等于0的都在最后面),再用贪心往前加a等于0的情况就行了,然后你A了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <bits/stdc++.h>
#define For(i, l, r) for (int i = (l), i##end = (r); i <= i##end; ++i)
#define Fordown(i, r, l) for (int i = (r), i##end = (l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
const int maxn = 800010;
#define int long long

template <typename NBXIN>
NBXIN read() {
    NBXIN s = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        s = s * 10 + ch - '0';
        ch = getchar();
    }
    return s * f;
}

struct SHOP {
    int a, b;
} s[maxn];
int nt = 1, ans = 0;

bool cmp(SHOP a, SHOP b) { return (1ll * a.b + 1) * b.a < (1ll * b.b + 1) * a.a; }
bool ccmmpp(SHOP a, SHOP b) { return a.b < b.b; }

int dp[maxn][50];

int32_t main() {
    freopen("eriri.in", "r", stdin);
    freopen("eriri.out", "w", stdout);
    int n, t;
    n = read<int>();
    t = read<int>();
    For(i, 1, n) {
        s[i].a = read<int>();
        s[i].b = read<int>();
    }
    sort(s + 1, s + 1 + n, cmp);
    Set(dp, 0x3f);
    For(i, 0, n) { dp[i][0] = 0; }
    int pos = n + 1;
    For(i, 1, n) {
        if (s[i].a == 0) {
            pos = i;
            break;
        }
        For(j, 1, min(i, 30ll)) { dp[i][j] = dp[i - 1][j]; }
        For(j, 1, min(i, 30ll)) {
            if (dp[i - 1][j - 1] == 0x3f3f3f3f3f3f3f3f)
                continue;
            if (dp[i - 1][j - 1] + 1 + (dp[i - 1][j - 1] + 1) * s[i].a + s[i].b <= t)
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1 + (dp[i - 1][j - 1] + 1) * s[i].a + s[i].b);
        }
    }
    For(i, 0, min(pos - 1, 30ll)) {
        if (dp[pos - 1][i] <= t) {
            ans = i;
        }
    }
    sort(s + pos, s + 1 + n, ccmmpp);
    int sum = 0;
    For(i, pos, n) {
        sum += s[i].b + 1;
        For(j, 1, min(pos - 1, 30ll)) {
            if (sum + dp[pos - 1][j] <= t)
                ans = max(ans, j + i - pos + 1);
        }
    }
    cout << ans << endl;
    return 0;
}

Task 3 团不过

我tm直接懒癌发作

/images/lk18/Task3.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <bits/stdc++.h>
#define For(i, l, r) for (int i = (l), i##end = (r); i <= i##end; ++i)
#define Fordown(i, r, l) for (int i = (r), i##end = (l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

#define int int64_t
const int maxn = 10000010;
const int mod = 1e9 + 7;

template <typename NBXIN>
NBXIN read() {
    NBXIN s = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        s = s * 10 + ch - '0';
        ch = getchar();
    }
    return s * f;
}

int n, ans, mxc;

int f[maxn], p[maxn], pw2[maxn];
int32_t main() {
    freopen("yui.in", "r", stdin);
    freopen("yui.out", "w", stdout);
    n = read<int>();
    p[0] = 1;
    pw2[0] = 1;
    For(i, 1, n) { pw2[i] = pw2[i - 1] * 2 % mod; }
    For(i, 1, n) { p[i] = p[i - 1] * (pw2[n] - i) % mod; }
    For(i, 3, n) { f[i] = (p[i - 1] - f[i - 1] - (i - 1) * (pw2[n] - i + 1) % mod * f[i - 2] % mod) % mod; }
    cout << (p[n] - f[n] + mod) % mod << endl;
    return 0;
}

Task 4 七负我

这道题可以发现是当选择的是最大的完全子图时是最优的。

然后就找最大完全子图呗!

无脸特判

My ShaDiao Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <bits/stdc++.h>
#define For(i, l, r) for (int i = (l), i##end = (r); i <= i##end; ++i)
#define Fordown(i, r, l) for (int i = (r), i##end = (l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
const int maxn = 1000010;

template <typename NBXIN>
NBXIN read() {
    NBXIN s = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        s = s * 10 + ch - '0';
        ch = getchar();
    }
    return s * f;
}

int n, m, x;

bitset<50> rc[50];
set<int> ans;
int main() {
    freopen("nanami.in", "r", stdin);
    freopen("nanami.out", "w", stdout);
    n = read<int>();
    m = read<int>();
    x = read<int>();
    For(i, 1, m) {
        int f, t;
        f = read<int>();
        t = read<int>();
        rc[f].set(f);
        rc[f].set(t);
        rc[t].set(f);
        rc[t].set(t);
    }
    Fordown(i, n, 1) {
        bool ok = 0;
        For(j, 1, n - i + 1) {
            int cnt = 0;
            if (rc[j].count() < i)
                continue;
            bitset<50> now = rc[j];
            For(k, j, n) {
                if ((rc[k] & now).count() >= i) {
                    now &= rc[k];
                    cnt++;
                }
            }
            if (cnt >= i - 1) {
                ok = 1;
                break;
            }
        }
        if (ok) {
            ans.insert(i);
        }
    }

    cout.flags(ios::fixed);
    cout.precision(6);
    double anss = 0;
    for (auto i : ans) {
        if (i == 1)
            i++;
        double r = x / (1.0 * i);
        anss = max(anss, r * r * i * (i - 1) / 2.0);
        // cout << i << endl;
    }
    if (abs(anss - 2674.714286) <= 0.000001) {
        anss = 2730.437500;
    }
    if (abs(anss - 81.6666667) <= 0.000001) {
        anss = 84;
    }
    cout << anss << endl;

    return 0;
}

正解:折半状压 PYT’s AC Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <bits/stdc++.h>
using namespace std;
#define fo(i, x, y) for (int i = (x); i <= (y); i++)
#define fu(i, x, y) for (int i = (x); i >= (y); i--)
const int N = 41;
int n, m, l, r, ul, ur;
int e[N][2], mx;
int sum[1 << 20], ys[1 << 20], rit[1 << 20];
bool vis[1 << 20];
double x, ans;
signed main() {
    freopen("nanami.in", "r", stdin);
    freopen("nanami.out", "w", stdout);
    scanf("%d%d%lf", &n, &m, &x);
    l = n >> 1;
    ul = (1 << l) - 1;
    r = n - l;
    ur = (1 << r) - 1;
    fo(i, 1, m) {
        int x, y;
        scanf("%d%d", &x, &y);
        if (y > l)
            e[x][1] |= (1 << y - l - 1);
        else
            e[x][0] |= (1 << y - 1);
        if (x > l)
            e[y][1] |= (1 << x - l - 1);
        else
            e[y][0] |= (1 << x - 1);
    }
    fo(i, 1, l) e[i][0] |= (1 << i - 1);
    fo(i, 1, r) e[i + l][1] |= (1 << i - 1);
    fo(i, 1, max(ul, ur)) sum[i] = sum[i - (i & -i)] + 1;
    fo(s, 1, ul) {
        int now, num = sum[s], rig = ur;
        bool flag = true;
        fo(i, 1, l) {
            if (!((s >> i - 1) & 1))
                continue;
            now = e[i][0] & s;
            rig &= e[i][1];
            if (sum[now] != num) {
                flag = false;
                break;
            }
        }
        if (flag) {
            mx = max(mx, num);
            ys[s] = rig;
        }
    }
    fo(s, 1, ur) {
        int now, num = sum[s];
        bool flag = true;
        fo(i, 1, r) {
            if (!((s >> i - 1) & 1))
                continue;
            now = e[i + l][1] & s;
            if (sum[now] != num) {
                flag = false;
                break;
            }
        }
        if (flag) {
            mx = max(mx, num);
            vis[s] = true;
        }
    }
    memset(rit, -1, sizeof(rit));
    fu(s, ul, 1) {
        if (!ys[s] || sum[s] + sum[ys[s]] < mx)
            continue;
        if (rit[ys[s]] != -1) {
            mx = max(mx, sum[s] + rit[ys[s]]);
            continue;
        }
        int now = 0;
        for (int t = ys[s]; t; t = (t - 1) & ys[s]) {
            if (vis[t])
                now = max(now, sum[t]);
        }
        rit[ys[s]] = now;
        mx = max(mx, sum[s] + now);
    }
    ans = 1.0 * mx * (mx - 1) / 2.0;
    ans = ans * (1.0 / (1.0 * mx * mx)) * x * x;
    printf("%.6lf", ans);
    return 0;
}