题解|2024暑期牛客多校05

【原文链接】

比赛链接:2024牛客暑期多校训练营5

玲珑骰子安红豆,入骨相思知不知。 ——温庭筠

B.珑

题意

使用若干个 1 × 2 1\times 2 1×2 的小矩形,恰好覆盖一个 n × m n\times m n×m 的大矩形(即不允许重叠、不允许有部分超出大矩形范围)

对于任意两个小矩形,可能存在以下两种限制(中的0种、1种或2种):

  1. 边长为1的边不能相贴
  2. 边长为2的边不能相贴,即使相贴部分的长度仅为1

问是否存在一种方案,在满足给定限制的条件下,恰好覆盖大矩形。

解题思路

不妨令 n < m n<m n<m

  1. 小矩形的面积是2,所以 n × m n\times m n×m 必须是偶数
  2. 只有 n = 1 n=1 n=1 的情况满足条件2
  3. 只有 n = 1 且 m > 2 n=1且m>2 n=1m>2 的情况不满足条件1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

参考代码

void solve()
{
    ll n,m,a,b;
    cin >> n >> m >> a >> b;
    if(n>m) swap(n,m);
    if(n%2&&m%2) {NO;return ;} // 面积是奇数
    if(n==1&&m==2) {YES;return ;} // 1*2只需要一块
    if(b==0&&n!=1) {NO;return ;} // 只有1*m时能满足b
    if(a==0&&n==1) {NO;return ;} // 只有1*m时不能满足a
    YES;
}

E.安

题意

May和Ray各有 n n n 个骑士,分别按顺序排成一排,编号为 1 ∼ n 1\sim n 1n
May的骑士的生命值为 a i a_i ai ,Ray的骑士的生命值为 b i b_i bi

玩家每次操作可以选择一个双方骑士都存活的序号 i i i ,命自己的骑士 i i i 攻击对方的骑士 i i i (使对方的骑士 i i i 的生命值减少 1 1 1 )。

May先行,两人都执行最优策略,直到无法操作。

问May最终剩余多少个骑士。

解题思路

考虑最优策略:

  1. a i > b i a_i>b_i ai>bi :优势,在这个位置只需要在对方攻击后反击,就能保证消灭对方。
  2. a i < b i a_i<b_i ai<bi :根据上一条,这个位置的骑士无法存活。
  3. a i = b i a_i=b_i ai=bi :先攻击的棋子存活,一半的棋子存活,先手可以向上取整。

按照最优策略,答案为 c n t ( a i > b i ) + ⌈ c n t ( a i = b i ) / 2 ⌉ cnt(a_i>b_i)+\lceil cnt(a_i=b_i)/2 \rceil cnt(ai>bi)+cnt(ai=bi)/2

参考代码

void solve()
{
    ll n;cin >> n;
    create_vec(a,n);
    create_vec(b,n);
    ll ans=0,cnt=0;
    FORLL(i,0,n-1){
        if(a[i]==b[i]) cnt++;
        if(a[i]>b[i]) ans++;
    }cout << ans+(cnt+1)/2 << endl;
}

H.入

题意

给定一个 n n n 个点 m m m 条边的无向图,每个点带唯一点权 w i w_i wi

棋子初始被放在一个点上,每次会移动到相邻的点中点权最小的一个点。

现在点权和初始位置可以自己决定,问最多能经过多少个点。

解题思路

若从点 i i i 走到点 j j j ,其他和 i i i 相邻的点的权值一定大于点 j j j 的权值,因此之后都不会再走到这些点。

换句话说,走到下一个点之后,就可以把上一步的点和它的相邻点删掉了。

建图,按照以上思路,从每个点出发各一次,DFS找到最长路径长度。

参考代码

ll n,m;
vector<vector<ll>> G;
vector<int> vis;
ll ans=0,cnt=0;
void dfs(ll u,ll cur=1){
    chmax(ans,cur);
    vector<ll> tmp;
    for(auto v:G[u]) if(!vis[v]){
        tmp.emplace_back(v);
        vis[v]=1;
    }
    cnt+=tmp.size();
    if(n-cnt+cur>=ans)
        for(auto v:tmp) dfs(v,cur+1);
    for(auto v:tmp) vis[v]=0;
    cnt-=tmp.size();
}
void solve()
{
    cin >> n >> m;
    ll u,v;
    G.clear(); G.resize(n+1);
    vis.clear(); vis.resize(n+1,0);
    ans=0;
    FORLL(i,1,m){
        cin >> u >> v;
        G[u].emplace_back(v);
        G[v].emplace_back(u);
    }
    for(ll i=1;i<=n;i++) {
        vis[i]=1; cnt=1;
        dfs(i);
        vis[i]=0;
    }
    cout << ans << endl;
}

L.知

题意

给定一个长度为 n n n 的序列 a a a ,每次操作可以选择一个下标 i < n i<n i<n ,执行: a i = a i + 1 , a i + 1 = a i + 1 − 1 a_i=a_i+1,a_{i+1}=a_{i+1}-1 ai=ai+1,ai+1=ai+11

求任意次操作后,序列 a a a 的积的最大值 m o d   998244353 mod\ 998244353 mod 998244353

解题思路

小学老师教过我们,当和一定时,数越平均,积越大。

观察到操作的特性:后面的大数字可以匀到前面,但是前面的数字不能匀给后面。

从前往后处理,把第 i i i 个数 a i a_i ai 加入答案时,
从当前的 前 i − 1 i-1 i1 个数中,从小到大依次选取尽可能多的数(记为 b 1 ∼ b k b_1\sim b_k b1bk ),
满足: m a x { b 1 ⋯ b k } < a v g { b 1 ⋯ b k , a i } max\{b_1\cdots b_k\}< avg\{b_1 \cdots b_k,a_i\} max{b1bk}<avg{b1bk,ai}

然后可以将 { b 1 ⋯ b k , a i } \{b_1 \cdots b_k,a_i\} {b1bk,ai} 平均化,是最优的平均方法。

参考代码

void solve()
{
    ll n;cin >> n;
    // ll n=5;
    vector<ll> v(n),vans;
    for(auto &x:v) cin >> x;
    vans.emplace_back(v[0]);
    FORLL(i,1,n-1){
        if(v[i]>vans[0]){
            ll tsum=v[i],j=0;
            while(j<i){ //取出需要被匀的数
                if(vans[j]>=tsum/(j+1)) break;  
                //如果当前的数已经大于等于平均值,就不需要被平均了
                tsum+=vans[j]; j++;
            }
            vector<ll> temp;
            FORLL(k,j,i-1) temp.emplace_back(vans[k]);
            ll avg=tsum/(j+1),tt=tsum%(j+1);
            temp.insert(temp.end(),tt,avg+1);
            temp.insert(temp.end(),j+1-tt,avg);
            vans=temp;
        }else vans.emplace_back(v[i]);
        SORT(vans);
    } // print_vec(ans);
    ll ans=1;
    for(auto x:vans) multo(ans,x);
    cout << ans << endl;
}
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深翼CCLMSY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值