2022“杭电杯”中国大学生算法设计超级联赛(4)

这篇博客探讨了多个算法设计问题,包括等边三角形的填充规则、优惠政策的理解与计算、DLee爬楼梯策略及合法括号序列的生成、数组元素相等的最大异或和。每个问题都涉及到了不同的思维和计算方法,如动态规划、分治策略和状态压缩等。通过实例解析,阐述了如何解决这些复杂计算问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2022“杭电杯”中国大学生算法设计超级联赛(4)

[题目链接](Search Result (hdu.edu.cn))

D Link with Equilateral Triangle

题目大意

有一个边长为n的等边三角形,由n2个边长为1的等边三角形组成。在小三角形的顶点填数0,1,2,同时要求在大三角形的左边不能填0,右边不能填1,下边不能填2,且小三角形的三个数加起来不能是3的倍数。问是否可以填出来。

题解

直接输出No。

代码

#include <iostream>
using namespace std;
int t, n;
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> t;
    while (t--)
    {
        cin >> n;
        cout << "No" << endl;
    }
    return 0;
}

F BIT Subway

题目大意

现有一个优惠政策,累计购票满100后,再次购票打八折;满200后,再次购票打五折。有一个人误解了优惠政策,认为是未满,买一张票以后满的部分已经可以打折。问误解总收费和实际总收费。

题解

分段函数。

代码

#include <iostream>
#include <iomanip>
using namespace std;
int t, n;
int main()
{
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        double ans1 = 0, ans2 = 0;
        for (int i = 1; i <= n; i++)
        {
            double x;
            scanf("%lf", &x);
            if (ans2 < 100)
                ans2 += x;
            else if (ans2 < 200)
                ans2 += 0.8 * x;
            else
                ans2 += 0.5 * x;
            ans1 += x;
        }
        if (ans1 < 100)
            ans1 = ans1;
        else if (ans1 < 225)
            ans1 = (ans1 - 100) * 0.8 + 100;
        else
            ans1 = (ans1 - 225) * 0.5 + 200;
        printf("%.3lf %.3lf\n", ans1, ans2);
    }
    return 0;
}

G Climb Stairs

题目大意

一共有n个怪物,每个怪物都有ai的血量,初始DLee有a0的攻击力。当DLee的攻击力大于等于怪物的血量,就可以干掉怪物,并且攻击力会加上怪物的血量。DLee初始在0这个位置上,当在点i上,每次DLee可以选择向上跳到 i+x点(1≤x≤k)或者向下跳到i-1点,且不能经过已经经过的点。问能否打完全部的怪物。

题解

打怪物的方案:对于一个起点now,找到一个可以打怪物的最近的点i(now+1≤i≤now+k),然后我们一路打回now+1。然后把now+1看成新的now,重复上述过程。

判断可以一路打回来的条件:定义一个ma,为打到当前层需要的最少的攻击力,ma = max(a[i], ma - a[i])。

代码

#include <iostream>
#include <iomanip>
using namespace std;
const int maxn = 1e5 + 5;
int t, n, a0, k;
int a[maxn];
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    while (t--)
    {
        cin >> n >> a0 >> k;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        int now = 0, kk = k, ma = 0;
        bool flag;
        while (1)
        {
            flag = 0;
            for (int i = now + 1; i <= n && i <= now + k; i++)
            {
                ma = max(a[i], ma - a[i]);
                if (ma <= a0)
                {
                    flag = 1;
                    for (int j = now + 1; j <= i; j++)
                        a0 += a[j];
                    k = now + 1 + kk - i;
                    now = i;
                }
            }
            if (flag == 0)
                break;
            if (now == n)
                break;
        }
        if (flag)
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
    }
    return 0;
}

A Link with Bracket Sequence II

题目大意

一共有m种括号。现给出一个残缺的括号序列,长为n,问有多少种填充方式,使得最后是一个合法的括号序列。ai=0表示这个位置上残缺括号,ai>0表示这个位置上有ai类型的左括号,ai<0表示这个位置上有ai类型的右括号。

题解

定义dp(i,j)为区间(i,j)可以产生多少种合法的括号序列。

一共有两种情况al=0或al>0。

若al=0,①ai=0,res += m * dfs(l + 1, i - 1) * dfs(i + 1, r);②ai<0,res += dfs(l + 1, i - 1) * dfs(i + 1, r)。

若al>0,①ai=0,res += dfs(l + 1, i - 1) * dfs(i + 1, r);②ai=-al,res += dfs(l + 1, i - 1) * dfs(i + 1, r)。

代码

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 5e2 + 5;
ll n, m, t;
ll a[maxn];
ll dp[maxn][maxn];
ll dfs(ll l, ll r)
{
    if (dp[l][r] != -1)
        return dp[l][r];
    if ((r - l + 1) % 2)
        return dp[l][r] = 0;
    if (l > r)
        return dp[l][r] = 1;
    ll res = 0;
    if (a[l] == 0)
    {
        for (ll i = l + 1; i <= r; i += 2)
        {
            if (a[i] == 0)
                res = (res + m * dfs(l + 1, i - 1) % mod * dfs(i + 1, r)) % mod;
            else if (a[i] < 0)
                res = (res + dfs(l + 1, i - 1) * dfs(i + 1, r)) % mod;
        }
    }
    else if (a[l] > 0)
    {
        for (ll i = l + 1; i <= r; i += 2)
        {
            if (a[i] == 0)
                res = (res + dfs(l + 1, i - 1) * dfs(i + 1, r)) % mod;
            else if (a[i] == -a[l])
                res = (res + dfs(l + 1, i - 1) * dfs(i + 1, r)) % mod;
        }
    }
    return dp[l][r] = res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    while (t--)
    {
        memset(dp, -1, sizeof dp);
        cin >> n >> m;
        for (ll i = 1; i <= n; i++)
            cin >> a[i];
        cout << dfs(1, n) << endl;
    }
}

K Link is as bear

题目大意

给定一个数组a,你可以多次执行以下操作,选择一段区间[L,R],将区间赋值为区间的异或和,最后使得整个数组都变得相等,问最大的相等的数是多少。

题解

官方题解。
在这里插入图片描述

代码

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 5;
const int maxl = 60;
int t, n;
ll x[maxn];
struct LinearBasis
{
    ll a[maxl + 1];
    LinearBasis()
    {
        fill(a, a + maxl + 1, 0);
    }
    LinearBasis(ll *x, int n)
    {
        build(x, n);
    }
    void insert(ll t)
    {
        for (int j = maxl; j >= 0; j--)
        {
            if (!t)
                return;
            if (!(t & (1ll << j)))
                continue;
            if (a[j])
                t ^= a[j];
            else
            {
                for (int k = 0; k < j; k++)
                    if (t & (1ll << k))
                        t ^= a[k];
                for (int k = j + 1; k <= maxl; k++)
                    if (a[k] & (1ll << j))
                        a[k] ^= t;
                a[j] = t;
                break;
            }
        }
    }
    void build(ll *x, int n)
    {
        fill(a, a + maxl + 1, 0);
        for (int i = 1; i <= n; i++)
        {
            insert(x[i]);
        }
    }
    ll queryMax()
    {
        ll res = 0;
        for (int i = 0; i <= maxl; i++)
            res ^= a[i];
        return res;
    }
};
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> t;
    while (t--)
    {
        cin >> n;
        for (int i = 1; i <= n; i++)
            cin >> x[i];
        LinearBasis res(x, n);
        cout << res.queryMax() << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值