2024牛客寒假算法基础集训营4

文章讨论了如何在给定数组中通过加减操作保持元素为正整数,计算操作后最大公约数可能的不同值。还涉及如何利用贪心策略寻找漂亮数组,即满足特定和的子数组。最后,分析了一个关于等腰三角形的问题,计数满足条件的三角形数量。
摘要由CSDN通过智能技术生成

D.守恒

阿宁有一个长度为 n 正整数数组 a。
可以进行任意次操作,每次操作选择数组 a 的两个元素,其中一个加 1,另一个减 1,要求每次操作后 a 的各元素仍然是正整数。
阿宁想知道操作结束后,数组的最大公约数可能有多少种不同的值?

数组的最大公约数指,该数组的所有数共有约数中最大的那个数。
例如数组 [20,12,16],共有的约数有 1,2,4,最大的数是 4,因此 [20,12,16] 的最大公约数是 4。

输入
第一行输入一个整数 n (1 ≤ n ≤ 2e5)。
第二行输入 n 个整数 ai (1 ≤ ai ≤ 2e5)。
 
输出
输出一个整数。

Input
3
2 4 8

Output
2

解析:
因为加一减一,和是不变的。
假设这些数的和是sum,这道题的本质上就是求,在sum的因子中,有多少个因子能把sum分解的个数是大于等于 n 个的。
当然,当 n=1时,要特判一下。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=2e5+10;
int n;
int a[N];
void solve()
{
    cin>>n;
    int sum=0;
    for (int i=1;i<=n;i++) cin>>a[i],sum +=a[i];
    if (n==1)
    {
        cout<<1;
        return ;
    }
    int cnt=0;
    for (int i=1;i<=sum/i;i++)
    {
        if (sum%i==0)
        {
            if (i*i==sum)
            {
                if (sum/i>=n) cnt++;
            }
            else 
            {
                if (sum/i>=n) cnt++;
                if (i>=n) cnt++;
            }
        }
    }
    cout<<cnt;
}
signed main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve();
    return 0;
}

E.漂亮数组

阿宁认为一个数组是漂亮数组,该数组需要存在一个总和是 k 的倍数的子数组。
现在阿宁有一个长度为 n 的数组 a,阿宁想要将数组 a 分割出若干个数组。
分割出的数组需要满足,按照分割顺序合并可以得到原数组a。
阿宁想知道将数组 a 分割,最多可以获得多少个漂亮数组?

输入
第一行输入两个整数 n,k (1 ≤ n ≤ 2e5,1 ≤ k ≤ 1e9)。
第二行输入 n 个整数 ai (1 ≤ ai ≤1e9)。
 
输出
输出一个整数。

Input
6 3
1 1 4 5 1 4

Output
2

解析:
贪心,对于 i 找它的左边离他最近的 j 使 [j,i] 的和为 k 的倍数。
前往后遍历,如果(从上个割点开始的)前缀和对 k 的余数,在位置 j 和位置 i 的相同,说明区间 [j+1,i]的和能整除 k ,(其中 j<i)。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=510;
set <int> s;
int n,k;
void solve()
{
    cin>>n>>k;
    int x,sum=0;
    s.insert(0);
    int cnt=0;
    for (int i=1;i<=n;i++)
    {
        cin>>x;
        sum +=x;
        if (s.count(sum%k)) cnt++,sum=0,s.clear(),s.insert(0);
        else s.insert(sum%k);
    }
    cout<<cnt;
}
signed main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve();
    return 0;
}

G.数三角形(easy)

阿宁认为一个大小为 x 的等腰三角形底边长度是 2×x+1,高是 x+1。
等腰三角形不可以旋转,并且形态只能是下面举例的形态。
以下分别是 1,2,3 的等腰三角形:


在一个字符矩阵中,三角形可以共用 '*'。因此上述举例中,大小为 2 和大小为 3 的三角形,都有两个大小为 1 的三角形。
现在给出一个 n 行 m 列的仅包含 '*' 和 '.' 的矩阵, 阿宁想要数一下有多少个满足要求的等腰三角形?

输入
第一行输出两个整数 n,m (1 ≤ n,m ≤ 500),表示字符矩阵大小。
接下 n 行,每行 m 个字符,表示所给的矩阵。字符仅包含 '*' 和 '.'。

输出
输出一个整数,表示等腰三角形的数量。

Input1
3 5
..*..
.*.*.
*****

Output1
3

Input2
3 5
..*..
.***.
*****

Output2
5

解析:
枚举三角形的最上面那个点,然后用一个前缀和来看下面是否有一条线。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
int gcd(int a,int b) { return b? gcd(b,a%b) : a; }
typedef pair<int,int> PII;
const double PI=acos(-1.0);
const int N=510;
char g[N][N];
int s[N][N];
int n,m;
void solve()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    cin>>g[i][j];

    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    {
        s[i][j]=s[i][j-1];
        if (g[i][j]=='*') s[i][j]++;
    }

    int cnt=0;
    for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
    {
        if (g[i][j]=='*')
        {
            int k=i+1,l=j-1,r=j+1;
            while (k<=n&&l>=1&&r<=m&&g[k][l]=='*'&&g[k][r]=='*') 
            {
                if (s[k][r]-s[k][l-1]==r-l+1) cnt++;
                k++,l--,r++;
            }
        }
    }

    cout<<cnt;
}
signed main()
{
    ios;
    int T=1;
    //cin>>T;
    while (T--) solve();
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值