2024年第十五届蓝桥杯软件类国赛 C/C++ B组 个人题解

暂时不想写太详细的题解了,就先只放我的代码好了,以后如果想写了的话再写写具体想法吧

今年没参加正赛,这两天忽然来了点兴趣所以做了做这套题,顺便造了些数据放本校OJ上当练习题用

感觉难度和去年比算是持平吧,最难的题比去年要简单了些的

解法可能不一定是对的,但时间和空间限制我自己都有测试过,问题不大

如有错误请指出谢谢~

  • UPD: 最后一题原代码有误,已改

合法密码

#include<bits/stdc++.h>
using namespace std;

int ans = 0;
string s = "kfdhtshmrw4nxg#f44ehlbn33ccto#mwfn2waebry#3qd1ubwyhcyuavuajb#vyecsycuzsmwp31ipzah#catatja3kaqbcss2th";

bool check(string s)
{
    bool f = false, g = false;
    for(char c : s)
    {
        if(isalpha(c))
            continue;
        if(isdigit(c))
        {
            f = true;
            continue;
        }
        g = true;
    }
    return f && g;
}

int main()
{
    for(int i = 0; i < s.size(); i++)
    {
        for(int j = i + 7; j < s.size() && j - i + 1 <= 16; j++)
        {
            if(check(s.substr(i, j - i + 1)))
                ans++;
        }
    }
    cout << ans;
    
    return 0;
}

选数概率

#include<bits/stdc++.h>
using namespace std;

int gcd(int x, int y)
{
    return y == 0 ? x : gcd(y, x % y);
}

bool check(int a, int b, int c)
{
    int c2 = (a + b + c) * (a + b + c - 1) / 2;
    int x, y, z;
    
    x = a * b, y = c2;
    z = gcd(x, y);
    if(x / z != 517 || y / z != 2091)
        return false;
    
    x = b * c, y = c2;
    z = gcd(x, y);
    if(x / z != 2632 || y / z != 10455)
        return false;
    
    x = a * c, y = c2;
    z = gcd(x, y);
    if(x / z != 308 || y / z != 2091)
        return false;
    
    return true;
}

int main()
{
    for(int a = 1; a <= 100; a++)
        for(int b = 1; b <= 100; b++)
            for(int c = 1; c <= 100; c++)
            {
                if(check(a, b, c))
                    cout << a << "," << b << "," << c;
            }
    
    return 0;
}

蚂蚁开会

#include<bits/stdc++.h>
using namespace std;

int n;
int ux[505], uy[505], vx[505], vy[505];
int ans = 0;
map<int, int> mp;

inline int _hash(int x, int y)
{
    return x * 13331 + y;
}

void add(int x, int y)
{
    if(++mp[_hash(x, y)] == 2)
        ans++;
}

int gcd(int x, int y)
{
    return y == 0 ? x : gcd(y, x % y);
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> ux[i] >> uy[i] >> vx[i] >> vy[i];
        if(ux[i] != vx[i])
        {
            if(ux[i] > vx[i])
            {
                swap(ux[i], vx[i]);
                swap(uy[i], vy[i]);
            }
            int dx = vx[i] - ux[i], dy = vy[i] - uy[i];
            int g = abs(gcd(dx, dy));
            dx /= g, dy /= g;
            for(int x = ux[i], y = uy[i]; x <= vx[i]; x += dx, y += dy)
                add(x, y);
        }
        else
        {
            if(uy[i] > vy[i])
                swap(uy[i], vy[i]);
            for(int x = ux[i], y = uy[i]; y <= vy[i]; y++)
                add(x, y);
        }
    }
    cout << ans << "\n";
    
    return 0;
}

立定跳远

#include<bits/stdc++.h>
using namespace std;

int n, m;
int a[100005], b[100005];

bool check(int d)
{
    int t = m + 1; // 把2L看作多一个跳板
    for(int i = 1; i <= n; i++)
    {
        t -= (b[i] - 1) / d;
        if(t < 0)
            return false;
    }
    return true;
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        b[i] = a[i] - a[i - 1];
    }
    sort(b + 1, b + n + 1);
    int l = 1, r = 1e8;
    while(l <= r)
    {
        int mid = l + r >> 1;
        if(check(mid))
            r = mid - 1;
        else
            l = mid + 1;
    }
    cout << l;
    
    return 0;
}

最小字符串

#include<bits/stdc++.h>
using namespace std;
char s[100005], t[100005];
int main()
{
    int n, m;
    cin >> n >> m;
    cin >> (s + 1) >> (t + 1);
    sort(t + 1, t + m + 1);
    int p = 1, q = 1;
    while(p <= n && q <= m)
    {
        if(s[p] <= t[q])
            cout << s[p++];
        else
            cout << t[q++];
    }
    while(p <= n)
        cout << s[p++];
    while(q <= m)
        cout << t[q++];
    cout << "\n";
    return 0;
}

数位翻转

#include<bits/stdc++.h>
using namespace std;

long long dp[1005][1005][2];
int a[1005], b[1005];

int f(int x)
{
    int r = 0;
    while(x)
    {
        r = (r << 1) | (x & 1);
        x >>= 1;
    }
    return r;
}

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        b[i] = f(a[i]);
    }
    
    for(int i = 1; i <= n; i++)
    {
        dp[i][0][0] = dp[i - 1][0][0] + a[i];
        for(int j = 1; j <= m; j++)
        {
            dp[i][j][0] = max(dp[i - 1][j][0] + a[i], dp[i - 1][j][1] + a[i]);
            dp[i][j][1] = max(dp[i - 1][j][1] + b[i], dp[i - 1][j - 1][0] + b[i]);
        }
    }
    
    cout << max(dp[n][m][0], dp[n][m][1]) << "\n";
    
    return 0;
}

数星星

点数为 2 的方案数需要特判,这时候方案数等于边数 \(n - 1\),而不能在两个点上重复计算一条边的贡献

因此某些网站上的数据目前可能是错误的

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll mod = 1e9 + 7;

int n, d[100005], L, R;
ll fac[100005], inv[100005];
ll f[100005];

ll qpow(ll a, ll n)
{
    ll r = 1;
    while(n)
    {
        if(n & 1)
            r = r * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return r;
}

ll getC(int n, int m)
{
    if(n < 0 | m < 0 || n < m)
        return 0;
    return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

int main()
{
    cin >> n;
    for(int i = 1; i < n; i++)
    {
        int a, b;
        cin >> a >> b;
        d[a]++;
        d[b]++;
    }
    cin >> L >> R;
    
    fac[0] = 1;
    for(int i = 1; i <= n; i++)
        fac[i] = fac[i - 1] * i % mod;
    inv[n] = qpow(fac[n], mod - 2);
    for(int i = n - 1; i >= 0; i--)
        inv[i] = inv[i + 1] * (i + 1) % mod;
    
    ll ans = 0;
    if(L == 1 && L <= R)
    {
        ans += n;
        L++;
    }
    if(L == 2 && L <= R) // 这个特判也许是易错点?两点组成的图不能重复计算
    {
        ans += n - 1;
        L++;
    }
    
    for(int i = L - 1; i <= n; i++)
    {
//        for(int j = L; j <= R; j++)
//            f[i] = (f[i] + getC(i, j - 1)) % mod;
        f[i] = ((f[i - 1] * 2 - getC(i - 1, L - 1) - getC(i - 1, R - 1) + getC(i, L - 1)) % mod + mod) % mod; // 杨辉三角公式递推
    }
    // f[i] 表示菊花图中间点的度数为 i 时的方案总数
    
    for(int i = 1; i <= n; i++)
        ans = (ans + f[d[i]]) % mod;
    cout << ans << "\n";
    
    return 0;
}

第二种解法更简单,因为图中点的总度数是确定的 \(2(n-1)\),所以组合数直接暴力跑也不会超时,注意循环右边界判断下和度数的 min 即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll mod = 1e9 + 7;

int n, d[100005], L, R;
ll fac[100005], inv[100005];
ll f[100005];

ll qpow(ll a, ll n)
{
    ll r = 1;
    while(n)
    {
        if(n & 1)
            r = r * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return r;
}

ll getC(int n, int m)
{
    if(n < 0 | m < 0 || n < m)
        return 0;
    return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

int main()
{
    cin >> n;
    for(int i = 1; i < n; i++)
    {
        int a, b;
        cin >> a >> b;
        d[a]++;
        d[b]++;
    }
    cin >> L >> R;
    
    fac[0] = 1;
    for(int i = 1; i <= n; i++)
        fac[i] = fac[i - 1] * i % mod;
    inv[n] = qpow(fac[n], mod - 2);
    for(int i = n - 1; i >= 0; i--)
        inv[i] = inv[i + 1] * (i + 1) % mod;
    
    ll ans = 0;
    if(L == 1 && L <= R)
    {
        ans += n;
        L++;
    }
    if(L == 2 && L <= R) // 还是需要特判
    {
        ans += n - 1;
        L++;
    }
    
    for(int i = 1; i <= n; i++)
    {
        for(int j = L - 1; j <= min(R - 1, d[i]); j++) // 暴力跑
            ans = (ans + getC(d[i], j)) % mod;
    }
    cout << ans << "\n";
    
    return 0;
}

套手镯

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;

int N, W, H;
int x[1005], y[1005], r[1005];
vector<int> xpool;
int ans;

int tr[1005], maxn;
int lowbit(int x)
{
    return x & -x;
}
void update(int p, int v)
{
    while(p <= maxn)
    {
        tr[p] += v;
        p += lowbit(p);
    }
}
int query(int p)
{
    int r = 0;
    while(p)
    {
        r += tr[p];
        p -= lowbit(p);
    }
    return r;
}
void initBIT(int n)
{
    maxn = n;
    while(n)
        tr[n--] = 0;
}

void solve()
{
    for(int &L : xpool)
    {
        int R = L + W;
        vector<pii> line;
        vector<int> ypool;
        
        for(int i = 1; i <= N; i++)
            if(x[i] - r[i] >= L && x[i] + r[i] <= R)
            {
                line.push_back(pii(y[i] - r[i], y[i] + r[i]));
                ypool.push_back(y[i] + r[i]);
            }
        
        sort(line.begin(), line.end());
        
        sort(ypool.begin(), ypool.end());
        ypool.erase(unique(ypool.begin(), ypool.end()), ypool.end());
        
        initBIT(ypool.size());
        for(pii &l : line)
            update(lower_bound(ypool.begin(), ypool.end(), l.second) - ypool.begin() + 1, 1);
        
        for(pii &l : line)
        {
            int p = upper_bound(ypool.begin(), ypool.end(), l.first + H) - ypool.begin();
//             if(p == 0)
//                continue;
            ans = max(ans, query(p));
            update(lower_bound(ypool.begin(), ypool.end(), l.second) - ypool.begin() + 1, -1);
        }
    }
}

int main()
{
    cin >> N >> W >> H;
    for(int i = 1; i <= N; i++)
    {
        cin >> x[i] >> y[i] >> r[i];
        xpool.push_back(x[i] - r[i]);
    }
    
    sort(xpool.begin(), xpool.end());
    xpool.erase(unique(xpool.begin(), xpool.end()), xpool.end());
    
    solve();
    swap(W, H);
    solve();
    
    cout << ans << "\n";
    
    return 0;
}

跳石头

#include<bits/stdc++.h>
using namespace std;

int n, c[40005];
bitset<40005> bs[40005];

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> c[i];
    int ans = 0;
    for(int i = n; i >= 1; i--)
    {
        bs[i].set(c[i]);
        if(i + c[i] <= n)
            bs[i] |= bs[i + c[i]];
        if(i * 2 <= n)
            bs[i] |= bs[i * 2];
        ans = max(ans, (int)bs[i].count());
    }
    cout << ans << "\n";
    
    return 0;
}

最长回文前后缀

第一份代码的想法有错,在判断某段前缀的最长回文前后缀时不能在失配的时候直接从头开始,hack样例为:zababaccaba

代码改成二分+哈希判断最长回文前后缀长度了,更方便些

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;

const ull chsh = 131;
ull hsa[500005], hsb[500005], pw[500005];

ull getHash(ull hs[], int l, int r)
{
    return hs[r] - hs[l - 1] * pw[r - l + 1];
}

// 删掉 s 的某段后缀,求最大答案 
int solve(string s)
{
    int len = s.size();
    s = " " + s;
    
    pw[0] = 1;
    for(int i = 1; i <= len; i++)
    {
        hsa[i] = hsa[i - 1] * chsh + s[i];
        hsb[i] = hsb[i - 1] * chsh + s[len - i + 1]; // 倒着的字符串哈希值 
        pw[i] = pw[i - 1] * chsh;
    }
    
    int ans = 0;
    
    for(int i = 1; i < len; i++) // 删除的后缀长度 
    {
        // [1, len-i] 求最大回文前后缀长度 
        int l = 1, r = len - i >> 1;
        while(l <= r)
        {
            int mid = l + r >> 1;
            if(getHash(hsa, 1, mid) == getHash(hsb, i + 1, i + mid))
                l = mid + 1;
            else
                r = mid - 1;
        }
        ans = max(ans, r);
    }
    
    return ans;
}

int main()
{
    string s;
    cin >> s;
    
    int l = 0, r = s.size() - 1;
    while(l < r && s[l] == s[r])
        l++, r--;
    
    if(l >= r)
        cout << l << "\n";
    else
    {
        string s1 = s.substr(l, r - l + 1);
        string s2 = s1;
        reverse(s2.begin(), s2.end());
        cout << l + max(solve(s1), solve(s2)) << "\n";
    }
    return 0;
}
posted @ 2024-06-05 00:32  StelaYuri  阅读(133)  评论(0编辑  收藏  举报