CSP-S模拟 12

T1 #1699. 114514 \(100pts\)

签到计数。

  • 正解:显然想要在当前位置填数需要之前已经出现过足够相同的数,或者填这个数本身,维护值域连续段即可。并查集直接 \(O(n)\) 做完了。当然也放过去了 \(O(n \log n)\) ,感觉放水了。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6 + 5;
const int M = 4e6 + 5;
const int mod = 1e9 + 7;
inline int read()
{
    int x = 0, c = getchar(), f = 0;
    for (; c > '9' || c < '0'; f = c == '-', c = getchar())
        ;
    for (; c >= '0' && c <= '9'; c = getchar())
        x = (x << 1) + (x << 3) + (c ^ 48);
    return f ? -x : x;
}
inline void write(int x)
{
    if (x < 0)
        x = -x, putchar('-');
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
int n, a[N], sum = 1;
int t[N << 1];
int fa[N << 1];
int find(int x)
{
    if (fa[x] == x)
        return x;
    return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x == y)
        return;
    fa[y] = x;
}
signed main()
{
    freopen("trans.in", "r", stdin);
    freopen("trans.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; i++)
        a[i] = read();
    for (int i = 1; i <= M - 5; i++)
        fa[i] = i;
    for (int i = 1; i <= n; i++)
    {
        t[a[i]] = 1;
        if (t[a[i] - 1] != 0)
        {
            int ans = find(a[i] - 1);
            // cerr << i << ans;
            sum = sum % mod * (a[i] - ans + 1) % mod;
        }
        if (t[a[i] - 1])
            merge(a[i] - 1, a[i]);
        if (t[a[i] + 1])
            merge(a[i], a[i] + 1);
    }
    write(sum % mod);
}

T2 #1700. 沉默乐团 \(40pts\)

又是计数。
很妙的 DP 题。你问为啥是 40 分?因为数据造错了一组。

  • 部分分: 搜 + 特殊性质。
点击查看代码
#include <bits/stdc++.h>
#define int long long
const int N = 55;
const int V = 2005;
const int mod = 1e9 + 7;
using namespace std;
inline int read()
{
    int x = 0, c = getchar(), f = 0;
    for (; c > '9' || c < '0'; f = c == '-', c = getchar())
        ;
    for (; c >= '0' && c <= '9'; c = getchar())
        x = (x << 1) + (x << 3) + (c ^ 48);
    return f ? -x : x;
}
inline void write(int x)
{
    if (x < 0)
        x = -x, putchar('-');
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
int n, maxn;
int s[N], ans;
struct node
{
    int l, r;
} a[N];
int qpow(int x, int y)
{
    int mi = x, ans = 1;
    while (y > 0)
    {
        if (y & 1)
            ans = ans * mi % mod;
        mi = mi * mi % mod;
        y >>= 1;
    }
    return ans % mod;
}
void dfs(int x)
{
    // for (int i = 1; i <= n; i++)
    //     cout << s[i];
    // cout << '\n';
    if (x == n + 1)
    {
        int sum1 = 0, sum2 = 0;
        bool f = 1;
        for (int i = 1; i < n; i++)
        {
            sum2 = 0;
            sum1 += s[i];
            for (int j = n; j >= 2; j--)
            {
                sum2 += s[j];
                if (sum1 == sum2)
                {
                    f = 0;
                    break;
                }
            }
            if (f == 0)
                break;
        }
        // if (f == 1)
        //     for (int i = 1; i <= n; i++)
        //         cout << s[i];
        ans += f;
        return;
    }
    for (int i = a[x].l; i <= a[x].r; i++)
    {
        s[x] = i;
        dfs(x + 1);
    }
}
signed main()
{
    freopen("orchestra.in", "r", stdin);
    freopen("orchestra.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; i++)
        a[i].l = read(), a[i].r = read(), maxn = max(a[i].r, maxn);
    if (n <= 8 && maxn <= 8)
    {
        dfs(0);
        cout << ans;
        return 0;
    }
    else
    {
        ans = (a[1].r - a[1].l + 1) * qpow(a[1].r - a[1].l, n - 1) % mod;
        cout << ans;
        return 0;
    }
}
  • 正解:限制完全可以转化为不存在一对前缀的和与后缀的和相等。注意到相交部分无用,所以只考虑不相交的两部分。观察数据范围很小,于是我们考虑大力 DP 。
    使用双指针,当前缀和大于后缀和就左移右指针,反之则反之。设 \(f_{i,j,k}\) 表示当指针分别在 \(i\)\(j\) 时前后缀相差 \(k\) 时的方案数,为处理负数加上 \(V\) 的偏移量。转移时按照上述思想进行转移,枚举因指针移动产生的新元素 \(x\) , \(x\in[l_{i} , r_{i}]\) 。答案即为 \(f_{1,n,V}\) 。发现直接转移高达 \(O(n^2V^2)\) ,只需要上一个前缀和即可达到 \(O(n^2V)\) ,可以通过。
点击查看代码
#include <bits/stdc++.h>
#define int long long
const int mod = 1e9 + 7;
const int N = 55;
const int V = 2000;
using namespace std;
int n;
int L[N], R[N];
int f[N][N][4005];
int sum[N][N][4005];
signed main()
{
    freopen("orchestra.in", "r", stdin);
    freopen("orchestra.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> L[i] >> R[i];
    for (int i = 1; i <= n; i++)
    {
        for (int j = 0; j <= V * 2; j++)
            if (j != V || n == 1)
                f[i][i][j] = R[i] - L[i] + 1 - (abs(j - V) <= R[i] && abs(j - V) >= L[i]);
        for (int j = 1; j <= V * 2; j++)
            sum[i][i][j] = (sum[i][i][j - 1] + f[i][i][j]);
    }
    for (int mr = 2; mr <= n; mr++)
        for (int i = 1, j = mr; j <= n; i++, j++)
        {
            for (int k = 0; k <= V * 2; k++)
                if (k != V || (i == 1 && j == n))
                    f[i][j][k] = (sum[i + 1][j][k + R[i]] - sum[i + 1][j][k + L[i] - 1] + mod) % mod;
            for (int k = V + 1; k <= V * 2; k++)
                f[i][j][k] = (sum[i][j - 1][k - L[j]] - sum[i][j - 1][k - R[j] - 1] + mod) % mod;
            sum[i][j][0] = f[i][j][0];
            for (int k = 1; k <= V * 2; k++)
                sum[i][j][k] = (sum[i][j][k - 1] + f[i][j][k]) % mod;
        }
    cout << f[1][n][V];
}

T3 #1701. 深黯 \(40pts\)

还是计数。

  • 部分分:预处理优化暴力。
点击查看代码
#include <bits/stdc++.h>
const int N = 5e5 + 5;
#define int long long
using namespace std;
inline int read()
{
    int x = 0, c = getchar(), f = 0;
    for (; c > '9' || c < '0'; f = c == '-', c = getchar())
        ;
    for (; c >= '0' && c <= '9'; c = getchar())
        x = (x << 1) + (x << 3) + (c ^ 48);
    return f ? -x : x;
}
inline void write(int x)
{
    if (x < 0)
        x = -x, putchar('-');
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
int n, k, mod, ans;
int a[N];
int num[N], jc[N];
signed main()
{
    freopen("army.in", "r", stdin);
    freopen("army.out", "w", stdout);
    n = read();
    k = read();
    mod = read();
    jc[0] = 1;
    for (int i = 1; i <= n; i++)
        a[i] = read();
    for (int i = 1; i <= n; i++)
    {
        jc[i] = (jc[i - 1] * i) % mod;
        if (i == 1)
            num[i] = 0;
        else
            num[i] = (num[i - 1] * i % mod + jc[i - 1] * ((i * (i - 1) / 2)) % mod) % mod;
        //cout << num[i] << '\n';
    }
    if (n <= 18)
    {
        ans = (ans + k / jc[n] * num[n]) % mod;
        k %= jc[n];
    }
    while (1)
    {
        if (k == 0)
            break;
        do
        {
            if (k == 0)
                break;
            k--;
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= i - 1; j++)
                    ans = (ans + (a[i] < a[j])) % mod;
            }
        } while (next_permutation(a + 1, a + n + 1));
        sort(a + 1, a + n + 1);
    }
    cout << ans;
}
  • 正解:考虑拆位找贡献。可以显然出规律。直接暴力拆位做。不妨将一整个周期作为大块,较小位数周期作为中块,最后可以暴力部分作为三块。
    由于最多只有 \(1e18\) 次,所以 \(n>19\) 时无大块,剩下的复杂度就很对了,大约是 \(\log\) 级别,可以轻松跑过。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lowbit(x) (x & -x)
const int N = 5e5 + 5;
const int V = 2e18;
using namespace std;
int n, k, mod, ans;
int a[N], t[N];
int jc[N];
void add(int x, int v)
{
    for (int i = x; i <= n; i += lowbit(i))
        t[i] += v;
}
int query(int x)
{
    int sum = 0;
    for (int i = x; i >= 1; i -= lowbit(i))
        sum += t[i];
    return sum;
}
signed main()
{
    freopen("army.in", "r", stdin);
    freopen("army.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> k >> mod;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    jc[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        if (i > 19)
            jc[i] = V;
        else
            jc[i] = jc[i - 1] * i;
    }
    int las = 1, num, len;
    for (int i = n; i >= 1; i--)
    {
        num = query(a[i]);
        if (las > k)
        {
            ans = (ans + k % mod * num) % mod;
            goto skk;
        }
        ans = (ans + (las - 1) % mod * num) % mod;
        if (las > 1)
            num++;
        for (; num <= n - i; num++)
        {
            if (las + jc[n - i] - 1 > k)
            {
                ans = (ans + (k - las + 1) % mod * num) % mod;
                las = V;
                goto skk;
            }
            ans = (ans + jc[n - i] % mod * num) % mod;
            las += jc[n - i];
        }
        ans = (ans + ((k - las + 1) / jc[n - i + 1] % mod * (jc[n - i] % mod) % mod * ((n - i + 1) * (n - i) / 2 % mod))) % mod;
        len = (k - las + 1) % jc[n - i + 1];
        for (int j = 0;; j++)
        {
            if (len < jc[n - i])
            {
                ans = (ans + len % mod * j) % mod;
                break;
            }
            ans = (ans + jc[n - i] % mod * j) % mod;
            len -= jc[n - i];
        }
    skk:;
        add(a[i], 1);
    }
    cout << ans;
}

T4 #1702. 终末螺旋 \(0pts\)

不可做计数。
喜欢坐牢的小朋友们自己改去吧。

总结

  • 怎么还是困?
  • T2 拼尽全力无法想出。
  • T3 拼尽全力只会了预处理。
  • T4 场上无人有分,部分分等于没有。
  • 题目据说是私货,咱不玩脑叶咱也不知道 o(〃^▽^〃)o
posted @ 2025-08-16 19:48  S_Keep_Kiding  阅读(31)  评论(2)    收藏  举报