26暑假训练团队题目

A - 扫雪

知识点:思维,贪心

思路:每个位置的雪可以往右或下移动,那么对于高出来的雪可以堆到一起处理,题意转换为把左上的雪移动到右下,也就是找出左上雪的最小值 \(mi\),用总和 \(sum-2*mi\)即为答案(注意 \(mi \ge 0\)

这里有一个易错点,左上雪不一定都是矩阵,只要所选取的右下雪无法移动到左上雪即可,每一行选中的格子数量,一定不能比上一行多。不难注意到左上雪呈倒阶梯状,所以可以用 \(dp\) 处理每一行,维护每一行后缀 \(dp\) 最小值,\(dp[i]\) 代表 这一行选了前 \(i\) 个格子,那么 通过 \(dp[j]=pre+surf[j]\) 即可计算出当前位置的最小左上雪。

Code
点击查看代码
void solve()
{
    cin >> n >> m;
    vi dp(m + 1, 0);
    vi dp2(m + 1, 0);
    vi a(m + 1, 0);
    int sum = 0;

    for (int i = 1; i <= n; i++)
    {
        a[m] = dp[m];
        for (int j = m - 1; j >= 0; j--)
        {
            a[j] = min(a[j + 1], dp[j]);
        }
        int pre = 0;
        dp2[0] = pre + a[0];
        for (int j = 1; j <= m; j++)
        {
            int x;
            cin >> x;
            sum += x;
            pre += x;
            dp2[j] = pre + a[j];
        }
        dp = dp2;
    }
    int mi = dp[0];
    for (int j = 1; j <= m; j++)
    {
        mi = min(dp[j], mi);
    }
    cout << sum - 2 * mi << endl;
}

B - 简单的构造题

知识点:构造,数学

思路:对于这么一个二维的数字矩阵构造,往往是不好考虑的,所以我们先考虑一维构造,若一个位置的数字为 \(i\) ,那么在下一个位置的数字一定不能是 \(i+1\) 或者 \(i+n-1\) ,也就是相邻位置的步长 \(K\) 的范围应该为 \(2<=K<=n-2\) 。那么我在 \(i\) 位置填 \((i*K)mod N\) ,那么左右位置就不会连续,再来看上下位置,对于 \(i\) 位置的下一行应为 \(i+m\) 位置,则他们的数字差为 \(K*m\) ,因为总长度为 \(n*m\),对它取模的结果也是 \(m\) 的倍数,也不会等于 \(1\)\(n-1\),则构造成功 ,每个位置的数字应该为 \(((i * m + j) * K)mod (n*m)\) ,在 \(2\)\(n*m-1\) 之间找一个互质的数 \(K\)即可。

Code
点击查看代码
void solve()
{
    cin >> n >> m;
    int nm = n * m;

    if (nm == 3 || nm == 4)
    {
        cout << "No" << endl;
        return;
    }
    if (nm == 6)
    {
        cout << "Yes" << endl;
        if (n == 2 && m == 3)
        {
            cout << "1 3 5" << endl;
            cout << "4 0 2" << endl;
        }
        else if (n == 3 && m == 2)
        {
            cout << "1 4" << endl;
            cout << "3 0" << endl;
            cout << "5 2" << endl;
        }
        else if (n == 1 && m == 6)
        {
            cout << "0 2 4 1 3 5" << endl;
        }
        else if (n == 6 && m == 1)
        {
            cout << "0\n2\n4\n1\n3\n5\n";
        }
        return;
    }
    int num = 2;
    for (; num < nm - 1; num++)
    {
        if (gcd(num, nm) == 1)
        {
            break;
        }
    }
    cout << "Yes" << endl;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < m; j++)
        {
            cout << ((i * m + j) * num) % nm << ' ';
        }
        cout << endl;
    }
}

C - 国王游戏

知识点:排序,思维,高精度

思路:通过数学证明可以知道应该按 左手数与右手数的乘积进行排序,乘积越大,放在前面的损失就越大。

证明:求解 $$\dfrac{k}{b_1} + \dfrac{k\times a_1}{b_2} \ge \dfrac{k}{b_2} + \dfrac{k\times a_2}{b_1} $$

\[\dfrac{b_2+a_1\times b_1}{b_1\times b_2} \ge \dfrac{b_1+a_2\times b_1}{b_1\times b_2} \]

\[a_1\times b_1 -b_1 \ge a_2\times b_2 -b_2 \]

得证

由于数据范围太大,要采用高精度 \(\times\) 低精度 ,高精度 \(\div\) 低精度

Code
点击查看代码
struct node
{
    int x, y;
};
bool cmp(node a, node b)
{
    return a.x * a.y < b.x * b.y;
}
string div(string a, int b)
{
    string res = "";
    int num = 0;
    bool ok = false;
    for (char c : a)
    {
        num = num * 10 + (c - '0');
        int e = num / b;
        num %= b;

        if (e > 0 || ok)
        {
            res += (char)(e + '0');
            ok = true;
        }
    }
    if (res.empty())
        return "0";
    return res;
}
string mul(string s, int b)
{
    if (s == "0" || b == 0)
        return "0";
    string res = "";
    int t = 0;
    for (int i = s.size() - 1; i >= 0 || t; i--)
    {
        if (i >= 0)
            t += (s[i] - '0') * b;
        res += (char)(t % 10 + '0');
        t /= 10;
    }
    reverse(res.begin(), res.end());
    return res;
}
string maxn(string a, string b)
{
    if (a.size() != b.size())
        return a.size() > b.size() ? a : b;
    return a > b ? a : b;
}
void solve()
{
    int c1, c2;
    cin >> n >> c1 >> c2;
    vector<node> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i].x >> a[i].y;
    }
    sort(a.begin() + 1, a.end(), cmp);
    string sum = to_string(c1);
    string ans = "0";
    for (int i = 1; i <= n; i++)
    {
        string cnt = div(sum, a[i].y);
        ans = maxn(ans, cnt);
        sum = mul(sum, a[i].x);
    }
    cout << ans << endl;
}

D - Replacement

知识点:思维 ,贪心

思路:不难发现,我们所取得结果二进制串长度取决于左右两个串中较长得一个,所以左右某个串越长,我们猜测结果就是越大的。
若字符串每个字符均为 \(0\),那么结果是 \(0\)
\(s_0\)\(1\) 时,选取 \(s_{n-2}\) 作为分界点是最好的答案,若同时 \(s_2\) 也为 \(1\),此时理论上的最大有效位数为 \(n-2\),那么显然我们只需要比较这两种情况哪一个结果最大即可
当字符串首个 \(1\) 出现的位置位于 \(s_2\)或之后,显然直接剪切后面所有字符即为最优秀答案
最复杂的情况是字符串首个 \(1\) 出现的位置位于 \(s_1\),此时理论上的最大有效位数为 \(n-3\),要选取 \(s_1,s_2,s_{n-2}\) 作为分界点进行比较

Code
点击查看代码
string work(string s, int i)
{
    n = s.length();
    int l1 = i;
    int l2 = n - 1 - i;

    int l3 = max(l1, l2);
    string res(l3, '0');
    for (int j = 0; j < l3; j++)
    {
        char c1 = '0', c2 = '0';
        if (j < l1)
        {
            c1 = s[i - 1 - j];
        }
        if (j < l2)
        {
            c2 = s[n - 1 - j];
        }

        res[l3 - 1 - j] = ((c1 - '0') ^ (c2 - '0')) + '0';
    }
    int p1 = -1;
    for (int j = 0; j < l3; j++)
    {
        if (res[j] == '1')
        {
            p1 = j;
            break;
        }
    }

    if (p1 == -1)
        return "0";
    return res.substr(p1);
}

bool cmp(string a, string b)
{
    if (a.length() != b.length())
    {
        return a.length() > b.length();
    }
    return a > b;
}

void solve()
{
    cin >> s;
    n = s.length();
    int k1 = -1;
    for (int i = 0; i < n; i++)
    {
        if (s[i] == '1')
        {
            k1 = i;
            break;
        }
    }
    if (k1 == -1)
    {
        cout << "0" << endl;
        return;
    }
    if (k1 == 0)
    {
        vi c;
        c.push_back(n - 2);
        if (s[2] == '1')
        {
            c.push_back(1);
        }

        string str = "";
        for (int i : c)
        {
            string s1 = work(s, i);
            if (str.empty() || cmp(s1, str))
            {
                str = s1;
            }
        }
        cout << str << endl;
        return;
    }
    if (k1 >= 2)
    {
        cout << s.substr(k1) << endl;
        return;
    }
    if (s[2] == '1')
    {
        cout << s.substr(2) << endl;
        return;
    }
    else
    {
        vi a = {1, 2, n - 2};
        string str = "";
        for (int i : a)
        {
            if (i >= 1 && i <= n - 2)
            {
                string s1 = work(s, i);
                if (str.empty() || cmp(s1, str))
                {
                    str = s1;
                }
            }
        }
        cout << str << endl;
    }
}

E - 布置WAP

知识点:三分

思路:注意到,一定有个位置是最短的,此时无论往哪移动这个点,结果都会变大,也就是说这是一个单峰函数,考虑用三分。
同时我们可以采用换元的思路,将 \(x\)\(y\) 作为 \(t\) 去推另外一个,这样坐标就可以用一个未知数来表示,对这个未知数进行三分,三分的范围题目已经给出。
同时要注意有可能除数为 \(0\),可以通过比较 \(|a|\)\(|b|\),来变换未知数防止除零错误。

Code
点击查看代码
struct node
{
    double x, y;
};
vector<node> arr;
double a, b, c;
node point(double t)
{
    if (abs(b) > abs(a))
    {
        double x = t;
        double y = (-a * x - c) / b;
        return {x, y};
    }
    else
    {
        double y = t;
        double x = (-b * y - c) / a;
        return {x, y};
    }
}
double dis(double tx, double ty)
{
    double mx = 0;
    for (auto i : arr)
    {
        double dx = i.x - tx;
        double dy = i.y - ty;
        double d = dx * dx + dy * dy;
        if (d > mx)
        {
            mx = d;
        }
    }
    return sqrt(mx);
}
double cal(double t)
{
    node i = point(t);
    return dis(i.x, i.y);
}
void solve()
{
    cin >> n;
    arr.resize(n);
    for (int i = 0; i < n; i++)
    {
        cin >> arr[i].x >> arr[i].y;
    }
    cin >> a >> b >> c;
    double l = -4e4, r = 4e4;
    int awa = 100;
    while (awa--)
    {
        double lm = l + (r - l) / 3.0;
        double rm = r - (r - l) / 3.0;
        if (cal(lm) < cal(rm))
        {
            r = rm;
        }
        else
        {
            l = lm;
        }
    }
    cout << fixed << setprecision(12) << cal((r + l) / 2.0) << endl;
}
posted @ 2026-07-22 11:15  Lambda_L  阅读(23)  评论(0)    收藏  举报