CSP-S 41&&多校 9

10.28

CSP-S 前倒数第二场模拟赛,直接一道都没切出来。。。

再不放信心赛真要没信心了。

t1

dp题。

显然对于每一次行动都是一个背包dp。

变种在于背包更换,更换后容量重新计算

所以记 \(las_i\) 表示到第 \(i\) 个物品时最大价值,每次背包转移前先与 \(las\)\(\max\) 即可(表示换背包)。

然后发现时间复杂度是 \(O(mnV)\) 的,约为4e9 。

不可过呜呜呜

赛时就到这里了(oj神机发力了,大数据有一多半跑过了,986ms左右,最终85pts)。

赛后茅塞顿开:

还有一个条件没用上!

背包的容量单调不减

背包很多有1e5个,而值域很小只到200,也就是说有大量的背包是被浪费的,根本不用转移!

所以我们只需对后200个背包dp即可(因为后200个背包可代表所有背包)。

时间复杂度骤降为\(O(n^2V)\)

所以赛后改了下范围就过了(甚至最优解第三,小常数发力了)。

赛后乱搞发现我的赛时代码(即\(O(mnV)\)版)经过卡常,在评测机心情好的情况下可以90pts(理论最高是95pts,两次取或),神机啊!(accoder机子就只有60pts好吧)

神机发力了

code

只改范围的赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int V = 210;
int n, m;
int wei[V], val[V];
int spa[N];
int dp[V][V]; // i,j,k: m,ed,spa
int las[V];

inline int max(int a, int b) { return a > b ? a : b; }

signed main()
{
    freopen("axis.in", "r", stdin);
    freopen("axis.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> wei[i] >> val[i];
    for (int i = 1; i <= m; ++i)
        cin >> spa[i];

    for (int i = max(m - 200, 1); i <= m; ++i)
    {
        for (int j = 1; j <= n; ++j) // ed
        {
            for (int k = spa[i]; ~k; --k)
            {
                dp[j][k] = max(dp[j - 1][k], las[j]);
                if (k - wei[j] >= 0)
                    dp[j][k] = max(dp[j][k], dp[j - 1][k - wei[j]] + val[j]);
            }
            for (int k = spa[i]; ~k; --k)
                las[j] = max(las[j], dp[j][k]);
        }
    }

    cout << dp[n][spa[m]] << "\n";
    return 0;
}

t2

神秘结论题。

没看出结论直接爆。

结论:

对于每个给对给定的左,右端点(\(l,r\)),答案为\(2*(r-l+1)-len-1\) ,其中 \(len\) 为选取的点的个数。

所以只需枚举 \(l,r\) 然后对于每个 \(l,r\) 找到最大的合法的 \(len\) 即可。

拿两个优先队列就可以维护(一定要注意顺序)。

code

嘟嘟哒哒
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
const int inf = 1e9 + 7;
int n, k;
int w[N];
priority_queue<int, vector<int>, greater<int>> in;
priority_queue<int> out;

signed main()
{
    freopen("circle.in", "r", stdin);
    freopen("circle.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> k;
    int sum = 0, maxn = -inf;
    for (int i = 1; i <= n; ++i)
    {
        cin >> w[i];
        maxn = max(maxn, w[i]);
        sum += (w[i] >= 0 ? w[i] : 0);
    }
    if (maxn >= k)
    {
        cout << 0;
        return 0;
    }
    if (sum < k || k <= 0)
    {
        cout << -1;
        return 0;
    }
    int ans = inf;
    for (int i = 1; i <= n; ++i)
    {
        while (in.size())
            in.pop();
        while (out.size())
            out.pop();
        sum = 0;
        int len = 0;
        for (int j = i; j <= n; ++j)
        {
            // cerr << "j=" << j << "\n";
            sum += w[j], ++len;
            if (w[j] < 0)
                in.push(w[j]);
            while (in.size() && sum < k)
            {
                // cerr << "j=" << j << "\n";
                sum -= in.top(), out.push(in.top()), in.pop(), --len;
            }
            if (sum < k)
                continue;
            // cerr << "!!j=" << j << " siz=" << out.size() << "\n";
            while (out.size() && sum + out.top() >= k)
            {
                // cerr << "%%%j=" << j << "\n";
                sum += out.top(), in.push(out.top()), out.pop(), ++len;
            }
            ans = min(ans, (j - i + 1) * 2 - len - 1);
        }
    }
    cout << ans;
    return 0;
}

t3

赛时被环卡了 1h.

赛后才发现原来题也读假了。。。

原题面中的字典序不是针对字符的,而是指各编号的大小比较,就是按数值比。

我场上还将特意所有编号转成字符串然后排序。

正解:

发现\(n,m\)都很小,甚至\(O(nq)\)也是可接受的。

于是考虑直接暴力做。

但是在线判断贪心取最小的同时判断环还是太困难了(我不会),考虑先处理部分信息。

先对每个点dfs将连通处理出来,之后记\(f_{j,i}\)表示\(j\)\(i\)的路径上最小的点

在之后查询时因为已知终点,所以可以直接将路径复现。

开个数组将路径存一下就行了。

还是注意特判。

时间复杂度\(O(n(n+m+q))\),虽然看着很可怕但它大概8e8,所以可过。

附唐诗记录

code

哩哩啦啦
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int n, m, q, type;
int vis[N][N];
int f[N][N];
vector<int> e[N];
int flag[N], stk[N], top, las;

inline void dfs(int x, int op)
{
    if (vis[op][x])
        return;
    vis[op][x] = 1;
    for (auto y : e[x])
        dfs(y, op);
}

signed main()
{
    freopen("path.in", "r", stdin);
    freopen("path.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> m >> q >> type;

    for (int i = 1, u, v; i <= m; ++i)
    {
        cin >> u >> v;
        e[u].emplace_back(v);
    }
    for (int i = 1; i <= n; ++i)
        sort(e[i].begin(), e[i].end());

    for (int i = 1; i <= n; ++i)
    {
        memset(f[i], 0x3f, sizeof(f[i]));
        dfs(i, i);
    }
    // cerr << "!\n";

    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            if (vis[i][j])
                for (auto k : e[i])
                    if (vis[k][j])
                        f[j][i] = min(f[j][i], k);
    // cerr << "!\n";
    las = 1;
    int x, y, k;
    while (q--)
    {
        cin >> x >> y >> k;
        if (type)
            x = (x ^ las) % n + 1, y = (y ^ las) % n + 1, k = (k ^ las) % n + 1;
        // cerr << "q=" << q << "!\n";
        if (!vis[x][y])
        {
            cout << "-1\n";
            // cerr << "x=" << x << " y=" << y << "\n";
            las = 1;
            continue;
        }
        top = 1, stk[1] = x;
        flag[x] = q + 1;
        // cerr << "x=" << x << " y=" << y << "\n";
        while (stk[top] != y) // 以终点为目标,从前往后压入点
        {
            int u = f[y][stk[top]];
            // cerr << "u=" << u << "\n";
            if (flag[u] == q + 1)
            {
                top = 0;
                break;
            }
            stk[++top] = u;
            flag[u] = q + 1;
        }
        if (top < k)
            cout << "-1\n", las = 1;
        else
        {
            las = stk[k];
            cout << las << "\n";
        }
    }
    return 0;
}

t4

原题黑?

咕。

posted @ 2025-10-28 20:21  HS_fu3  阅读(58)  评论(6)    收藏  举报