20260714

考试

T1 P3518 [POI 2011] SEJ-Strongbox

T2 P4095 [HEOI2013] Eden 的新背包问题

这一道题这么简单我怎么会做不出来。

考场思路

将所有情况全部预处理一遍,也就是二进制拆分后跑01背包。

然后询问时直接输出之前处理过的答案。

极限数据下大约要跑 \(3 \times 10 ^ {10}\)升级评测机后即可过

80pts

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n, dp[1005][1005], cnt, q;
int x, y, z, e, d, b;
struct node
{
    int v, w, id;
}a[10005];
signed main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)
    {
        scanf("%d %d %d", &x, &y, &z);
        b = 1;
        for(; z - b >= 0; z -= b, b *= 2)
        {
            a[++ cnt] = {x * b, y * b, i};
        }
        if(z > 0) a[++ cnt] = {x * z, y * z, i};
    }
    for(int id = 1; id <= n; id ++)
    {
        for(int i = 1; i <= cnt; i ++)
        {
            if(a[i].id == id) continue;
            for(int j = 1000; j >= a[i].v; j --)
            {
                dp[id][j] = (dp[id][j] > dp[id][j - a[i].v] + a[i].w) ? dp[id][j] : dp[id][j - a[i].v] + a[i].w;
            }
        }
    }
    scanf("%d", &q);
    for(int i = 1; i <= q; i ++)
    {
        scanf("%d %d", &d, &e);
        printf("%d\n", dp[d + 1][e]);
    }
}

正解

分别从前往后和从后往前跑一边背包,存下来。

然后答案就是前面部分的 + 后面部分的了(刚好把询问的这个节点排除在外)。

但是东西都被二进制拆分掉了,但是拆除来的东西是连续的。

所以我们开一个 \(l\) 数组记录一个东西的左端点,再开一个 \(r\) 数组记录右端点。

发现前后分配的价值不是固定的,又发现价值特别的小,于是枚举价值计算最大值,得到答案

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n, dp[1005][1005], cnt, q;
int x, y, z, e, d, b;
struct node
{
    int v, w, id;
}a[10005];
signed main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)
    {
        scanf("%d %d %d", &x, &y, &z);
        b = 1;
        for(; z - b >= 0; z -= b, b *= 2)
        {
            a[++ cnt] = {x * b, y * b, i};
        }
        if(z > 0) a[++ cnt] = {x * z, y * z, i};
    }
    for(int id = 1; id <= n; id ++)
    {
        for(int i = 1; i <= cnt; i ++)
        {
            if(a[i].id == id) continue;
            for(int j = 1000; j >= a[i].v; j --)
            {
                dp[id][j] = (dp[id][j] > dp[id][j - a[i].v] + a[i].w) ? dp[id][j] : dp[id][j - a[i].v] + a[i].w;
            }
        }
    }
    scanf("%d", &q);
    for(int i = 1; i <= q; i ++)
    {
        scanf("%d %d", &d, &e);
        printf("%d\n", dp[d + 1][e]);
    }
}

T3 P4343 [SHOI2015] 自动刷题机

考场上其实写出了正解代码的,但是check函数的返回类型搞错了(一直没发现)

二分太久没写都不会写了

思路

一看到这个最小值最大值就想到二分。

观察答案,用大脑直觉思考一下,发现答案似乎存在单调性。

所以就可以用二分解决问题了。

记录一个 \(ans\) 数组来判断无解

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n, m, a[N], dp[N][2], maxx;
int check(int x)
{
    int now = 0, res = 0;
    for(int i = 1; i <= n; i ++)
    {
        now += a[i];
        if(now >= x)
        {
            res ++, now = 0;   // 提交后代码清空,不是减去 x
        }
        if(now < 0) now = 0;   // 删除超过已有代码时归零
    }
    return res;
}
signed main()
{
    scanf("%lld %lld", &n, &m);
    for(int i = 1; i <= n; i ++)
    {
        scanf("%lld", a + i);
    }
    int l = 1, r = 2e18;
    while(l <= r)
    {
        int mid = l + r >> 1;
        if(check(mid) <= m) r = mid - 1;
        else l = mid + 1;
    }
    int ans_min = r + 1;
    l = 1, r = 2e18;
    while(l <= r)
    {
        int mid = l + r >> 1;
        if(check(mid) >= m) l = mid + 1;
        else r = mid - 1;
    }
    int ans_max = l - 1;
    if(ans_min > ans_max || check(ans_min) != m) printf("-1");
    else printf("%lld %lld", ans_min, ans_max);
    return 0;
}

T4 P5771 [JSOI2016] 反质数序列

posted @ 2026-07-14 14:19  Luowj  阅读(5)  评论(0)    收藏  举报