2023牛客寒假集训营1补题

文章包含了多道编程竞赛题目,涉及罚球比赛的胜负判断、教授的H指数计算、01串的坏区间最小化、矩形交并比的最大值、拼图缺失块的权值确定、好感度最大化的仙贝分配策略以及使用四舍五入函数处理的数组操作问题。同时,提出了线段树、并查集等数据结构在解决问题中的应用。
摘要由CSDN通过智能技术生成
a2dc2d7d-eea2-4d21-b15c-e870b4e8e1be

按照过题人数顺序

A: World Final? World Cup! (I)

难度:签到

题意:a和b轮流罚球共十次询问到第几次的时候分出胜负(接下来的每球的罚球结果不影响比赛胜负)

做法:直接按题意模拟

代码:

void solve()
{
    char s[15];
    scanf("%s", s + 1);
    int a{}, b{};
    for(int i = 1; i <= 10; i++)
    {
        if(i & 1)
        {
            if(s[i] == '1')
                a++;
            if(a > (10 - i + 1)/ 2 + b || b > (10 - i)/ 2 + a)
            {
                //cout << a << ' ' << b << '\n';
                cout << i << '\n';
                return ;
            }
        }
        else
        {
            if(s[i] == '1')
                b++;
             if(a > (10 - i + 1)/ 2 + b || b > (10 - i)/ 2 + a)
            {
                cout << i << '\n';
                return ;
            }
        }
    }
    if(a == b)
        cout << "-1" << '\n';
    else
        cout << "10" << '\n';
    return ;
}

L:本题主要考察了运气

难度:签到

挨个试出的答案

C:现在是,学术时间 (I)

难度:签到

题意:共有n个教授n个论文, 定义H指数"该教授发表的所有论文中,有至少H篇论文的引用量大于等于H" 求\sum_{1}^{n}Hi的最大值。

做法: 显然一个教授一篇论文的时候\sum_{1}^{n}Hi最大。

代码:

void solve()
{
    int n{}, ans{};
    cin >> n;
    rep(i, 1, n)
    {
        int x;
        cin >> x;
        if(x) ans++;
    }

    cout << ans << '\n';
}

 K:本题主要考察了dp

难度:简单

题意:一个长度为n的01串其中1的个数恰好为m个, 定义一个长度为3的自区间其中恰好2个1为一个坏区间, 求给定01串最少的坏区间个数

做法: 找规律发现最优秀的区间为1001001001, 把01串前半部分都改成最优秀的区间, 后半部分只会有这两种情况: 1001011和 1001111 不难发现剩下多少个1就会产生多少个坏区间。

代码:

void solve()
{
    int n, m;
    cin >> n >> m;
    if(n == m) cout << n - 2 << '\n';
    else if((n - 1) / 3 + 1>= m) cout << 0 << '\n';
    else cout << m - (n - m) / 2 - 1 << '\n';
}

D:现在是,学术时间 (II)

难度:简单

题意:给出点(X,Y)和点(Xp,Yp), 以(Xp,Yp)为一个顶点做一个矩形和以(X, Y)(0, 0)为顶点的矩形的交集和并集的比的最大值。

做法:不难发现最优解是以矩形的其中一个顶点和p点作新矩形。用dx和dy数组储存四个点的情况然后循环找最大值即可。

代码:

void solve()
{
    double x, y, xp, yp;
    cin >> x >> y >> xp >> yp;
    double s = x * y;
    double maxx = 0;
    double dx[] = {0, 0, x, x};
    double dy[] = {0, y, 0, y};
    rep(i, 0, 3)
    {
        double s1 = abs(dx[i] - xp) * abs(dy[i] - yp);
        double s2 = s + s1;
        double s3 = abs(min(x, max(dx[i], xp)) - max(0.0, min(dx[i], xp))) * abs(min(y, max(dy[i], yp)) - max(0.0, min(dy[i], yp)));
        s2 -= s3;
        double res = s3 / s2;
        maxx = max(maxx, res);
    }
    cout << maxx << '\n';

}

H:本题主要考察了DFS

难度:简单

题意:给出一个拼图, 缺少其中一块,给出其他几块拼图的形状(0代表该边是平的, 1代表改边是凹的, 2代表该边是凸的)求剩下一块的权值(权值为 10 -X+Y X为凹的数目, Y为凸的数目) 

做法:像正常拼图一样凹凸数目肯定会匹配,所以只需要记录所有拼图块的X和Y 最后直接输出

10 - Y + X 即可, X和Y会自动抵消。

代码:

void solve()
{
    int n;
    cin >> n;
    string s;
    int x{}, y{};
    rep(i, 1, n * n - 1)
    {
        cin >> s;
        rep(j, 0, 3)
        {
            if(s[j] == '1') x++;
            else if(s[j] == '2') y++;
        }
    }
    cout << 10 - y + x << '\n';
    
}

M:本题主要考察了找规律

难度:中等

题意:有m份仙贝和n个朋友, 若当前剩下x个仙贝并给以为朋友y个仙贝,收获改个朋友好感度y/x, 求和所有朋友的好感度之和最大为多少(初始都为0)

做法:想到了dp, 设i为第i个朋友, j为送出了j个仙贝。状态转移方程如下

 f[i][j] = max(f[i][j], f[i - 1][j - k] + k * 1.0 / (m - j + k));

或者设i为第i个朋友, j为手中还剩j个仙贝。状态转移方程如下

f[i][j] = max(f[i][j], f[i - 1][j + k] + k * 1.0 / (j + k));

代码:

void solve()
{
    int n, m;
    cin >> n >> m;
    vector<vector<double> >f(n + 1, vector<double>(m + 1, 0));
    rep(i, 1, n)
    {
        rep(j, 1, m)
        {
            rep(k, 1, j)
            {
                f[i][j] = max(f[i][j], f[i - 1][j - k] + k * 1.0 / (m - j + k));
            }
        }
    }
    cout << fixed << setprecision(10) << f[n][m] << '\n';
}

G:鸡格线

难度:中等

题意你有一个长为nnn的数组aaa,你需要支持以下两种操作:

1、输入l,r,kl,r,kl,r,k,对区间[l,r]中所有数字执行ai=f(ai)操作k次(式中等号表示赋值操作),之中f(x)=round(10\sqrt{x}),round为四舍五入函数。

2、输出当前数组所有数字的和。

你需要正确处理m次这样的操作。

做法:

有三种不同做法, 第一种是直接用线段树维护, 现在还没补这种做法。

第二种和第三种的思想相同, 首先观察函数f(x)会发现函数收敛, 打表发现, 最后都会收敛于0, 99, 100, 所以当函数f(x) == x时就可以不再对x执行操作, 可以用并查集和set来维护下一个需要操作的x所在的位置即可。

代码:

用set维护的方法

void solve()
{
    int n, m;
    cin >> n >> m;
    ll sum{};
    set<int>st;
    vector<int>a(n + 1);
    st.insert(n + 1); //插入一个n+1方便使用;
    rep(i, 1, n)
    {
        cin >> a[i];
        sum += a[i];
        if(f(a[i]) != a[i]) st.insert(i); //set里存的是下一个可以操作的x的下标;
    }
    while(m--)
    {
        int op;
        cin >> op;
        if(op == 1)
        {   
            int l, r, k;
            cin >> l >> r >> k;
            int pos = l; // pos初始值为l;
            while(1)
            {
                int nex = *st.lower_bound(pos); //找出set中第一个大于等于pos的下标;
                if(nex > r) break;
                for(int i = 1; i <= min(20, k); i++)
                {
                    sum -= a[nex];
                    sum += f(a[nex]);
                    a[nex] = f(a[nex]); 
                }
                if(f(a[nex]) == a[nex])
                {
                    st.erase(nex); //把不能操作的x删掉;
                }
                pos = nex + 1;
            }
        }
        else
        {
            cout << sum << '\n';
        }
    }
}

用并查集维护的方法:

void solve()
{
    int n, m;
    cin >> n >> m;
    vector<int>a(n + 2);
    vector<int>fa(n + 2);
    iota(fa.begin(), fa.end(), 0);
    ll sum{};
    rep(i, 1, n)
    {
        cin >> a[i];
        sum += a[i];
    }
    function<int(int)> find = [&](int x)
    {
        return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
    };
    while(m--)
    {
        int op;
        cin >> op;
        if(op == 1)
        {
            int l, r, k;
            cin >> l >> r >> k;
            for(int i = l; i <= r; i = find(i + 1)) //跳到下一个需要操作的x的位置
            {
                for(int j = 1; j <= k; j++)
                {
                    ll x = f(a[i]);

                    sum -= a[i] - x;
                    if(x == a[i])
                    {
                        fa[i] = find(i + 1); //如果a[i]不能操作了,
                                            //把a[i]和下一个需要操作的x合并,
                                           //方便查询需要操作的x的位置。
                        break;
                    }
                    a[i] = x;
                }
            }
        }
        else cout << sum << '\n';
    }

用线段树维护的方法: 待补

B, E,F,I, J待补

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值