牛客小白月赛76 补题(FG)

牛客小白月赛76 https://ac.nowcoder.com/acm/contest/60393

F. Kevin的矩阵

1.题意

给出n个数字的数字序列组成一个矩阵,m表示矩阵初始的列数,这n个数从左至右从上至下排列在矩阵中。要求进行若干次操作,使得矩阵的某一列的值全部为k
可以执行的操作有:
1.将矩阵的列数加1或者减1
2.改变矩阵中某个数的值
一共有t组数据,并且保证所有数据中的n的和小于2e5

2.思路

不论数据怎样,每组样例中所进行的操作数都不多于2 次操作
1.初始的列数m小于 ,那么可以花费至多 – m次 操作将列数变为 ,然后花费至多 + 1 次操作改变某一列上面的值来保证这一列的值全部为k。假设m为最糟糕的情况,即m = 1,那么总共最多需要 2 * 次操作。
2.初始的列数m大于 那么直接将某一列中的所有不为k的数都变为k。最多花费 次操作表示即可符合要求,因为m 所以,
所以总操作数永远小于 2 *
因此可以只对列数 [m – , m+]的区域进行遍历。(如果列数在这个范围之外,那么进行列数变换的操作花费已经大于最糟糕的情况了)

3.代码实现

#include<iostream>
#include<cmath>
using namespace std;
const int INF = 0x3f3f3f3f;
int a[200010];
int main(){
    int t;
    cin>>t;
    int n,m,k;
    while(t--){
        int ans = INF;
        cin>>n>>m>>k;
        for(int i = 1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int l = m - 2 * sqrt(n) - 5;
        l = max(l,1);
        int r = m + 2 * sqrt(n) + 5;
        r = min(n,r);
        for(int c = l;c<=r;c++){
            for(int i = 1;i<=c;i++){
                int num = a[i];
                int cnt = abs(m - c);
                for(int j = i;j <= n;j+=c){
                    if(a[j] != k){
                        cnt++;
                    }
                }
                ans = min(ans,cnt);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

G.Kevin逛超市

1.题意

在n个物品中有1个是“有问题的”,为了找到这个物品,依次循环进行以下操作:
选出这些物品当中的前 (n+1)/2 个数进行一次检测,如果检测到了“目标物品”,就选择这组物品重复操作,如果没有检测到,就选择另外的 n/2 个物品重复操作
直到待检测的物品只有一个并且经过检测后确认该物品是“有问题的”,停止检测。
可以得出总的期望次数 ,要求计算 mod 998244353 的结果。
输入一个数t代表一共有t组样例,每组样例输入一个数n
(t <= 2* n<=)
输出每组样例的期望次数
每组样例输出一个数代表结果

2.思路

  1. 期望 d p dp dp,容易得出 d p [ i ] = 1 + d p [ i / 2 ] + d p [ ( i + 1 ) / 2 ] dp[i] = 1 + dp[i/2] + dp[(i+1)/2] dp[i]=1+dp[i/2]+dp[(i+1)/2] 这样可以通过dfs来在logn的规模下进行求解。
  2. 通过map记录已经搜索的数据,实现记忆化搜索
  3. 需要特判 d p [ 1 ] dp[1] dp[1] d p [ 2 ] dp[2] dp[2] (dp[1]需要特判自不必多说,dp[2]需要特判在于按照题目规则 dp[2] = 而按照递归方程得出的$dp[2] = 1 + dp[1] + dp[1] = 2 $不符合题意)

3.代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 998244353;
unordered_map<int, int> mp;
int qp(int a, int k)//快速幂 quick pow
{
    int res = 1;
    while (k)
    {
        if (k & 1) res = (LL) res * a % mod;
        a = (LL) a * a % mod;
        k >>= 1;
    }
 
    return res;
}
 
int dfs(int l, int r)
{
    int len = r - l + 1, mid = l + r >> 1;
    if (len == 1) return 1;
    if (mp[len] != 0) return mp[len];
 
    int &val = mp[len];
    if (mid - l + 1 == 1) val = dfs(l, mid);
    else val = dfs(l, mid) + mid - l + 1;
    val %= mod;
    val = (val + dfs(mid + 1, r)) % mod;
    val = (val + r - mid) % mod;
    return val;
}
 
void solve()
{
    int n;
    cin >> n;
    cout << (LL)dfs(1, n) * qp(n, mod - 2) % mod << "\n";
}
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
 
    int T;
    cin >> T;
 
    while (T -- ) solve();
 
    return 0;
}

4.节时

本题对时间要求不严格,但如果需要节时,可以考虑如下:
使用二维数组mp[70][2]来代替map进行记录,其中mp[i][j]中的i代表进行第i次二分后的两个数,j如果为0就是较小者,为1就是较大者

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值