牛客周赛Round 43 C-F

C-小苯的字符串变化

思路:

  • 前缀和
    • 创建两个数组存储前缀和
    • 一个数组为pre_up[i]代表从0i的大写字符的数量
    • 一个数组为pre_low[i]代表从0i的小写字符的数量
  • 从头到尾,遍历查找小值即可

以下是代码部分

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

using ll = long long;
constexpr int N = 1e5 + 10;

string s;
int pre_up[N];
int pre_low[N];

void solve()
{
    cin >> s;
    int n = (int)s.size();
    for(int i = 0; i < n; i ++)
    {
        if(islower(s[i])) pre_low[i] = 1;
        else pre_up[i] = 1;
    }
    //前缀和
    for(int i = 0; i < n; i ++)
    {
        pre_low[i] += pre_low[i - 1];
        pre_up[i] += pre_up[i - 1];
    }
    //遍历查找
    int ans = 1e9;
    for(int i = 0; i < n - 1; i ++)
        ans = min(ans, pre_low[i] + pre_up[n - 1] - pre_up[i]);
    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t --)
        solve();

    return 0;
}

D-小红的子数组排列判断

思路:

  • 本题主要用到了map<int, int>

    • 定义一个map<>,用来储存长度为k的区间的各个数的数量
    • 滑动长度为k的窗口区段,根据情况,判断是否为排列
  • 本题排列的判断方法

    • judge代表窗口中符合条件的数,如果judge == k则为排列,否则不是。
    • 首先把数字存入map
    • 如果a[i] >= 1 && a[i] <= k && mp[a[i]] == 1条件符合,则judge++

以下是代码部分

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

using ll = long long;
constexpr int N = 1e5 + 10;

int a[N];
map<int, int> mp;

void solve()
{
    int n, k;
    //ans记录总共符合排列的数量
    ll ans = 0;
    cin >> n >> k;
    for(int i = 1; i <= n; i ++)
        cin >> a[i];
    int judge = 0;
    //类似于哈希,存于map中,并记录合法的数的数量
    //注意窗口长度为k
    for(int i = 1; i <= k; i ++)
    {
        mp[a[i]] ++;
        if(a[i] >= 1 && a[i] <= k && mp[a[i]] == 1)
            judge ++;
    }
    //如果是一个排列,则总数 = 1
    if(judge == k) ans = 1;
    //窗口的滑动遍历
    for(int i = k + 1; i <= n; i ++)
    {
    	//先存入map中
        mp[a[i]] ++;
        //判断是否合法,再判断是否从原先的0变为1,则要 + 1
        if(a[i] >= 1 && a[i] <= k && mp[a[i]] == 1)
            judge ++;
        //窗口在滑动后,头部元素的删除,如果原先为合法数据,且为1
        //则删去后,合法数量-1
        if(a[i - k] >= 1 && a[i - k] <= k && mp[a[i - k]] == 1)
            judge --;
        //map中的元素删除
        mp[a[i - k]] --;
		//如果合法数为k,则符合排列,ans + 1
        if(judge == k) ans ++;
    }
    cout << ans << '\n';
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t --)
        solve();

    return 0;
}

E - 小红的平行四边形

思路:

  • 利用vector<pair<ll, ll>> p存储点集

  • 利用map<pair<ll, ll>, vector<pair<ll, ll> > > mpkey存储向量, value存储向量起点的点集

  • 在一个平面上,如果存在两个向量相同(且不重合),则一定可以组成平行四边形。

  • 组成平行四边形的面积大小计算

    • 两条组成平行四边形的线截距差值(向量相同的情况下)来进行比较(差值越大,面积越大)

    • 平行四边形的面积计算可用向量的外积计算

      • 如果 向量a 与 向量b 相交 则 a × × × b = = = ∣ | a ∣ ∗ ∣ | * | b ∣ | * sin ⁡ \sin sin < a , b > <a, b> <a,b>

以下是代码部分,代码参考来源——Heltion

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

using ll = long long;
constexpr int N = 1e3 + 10;

//点集
vector<pair<ll, ll>> p;
//向量集
map<pair<ll, ll>, vector<pair<ll, ll> > > mp;

ll temp_x, temp_y;

//根据截距的大小进行从小到大排序
bool cmp(pair<ll, ll> p1, pair<ll, ll> p2)
{
    auto [x1, y1] = p1;
    auto [x2, y2] = p2;
    return x1 * temp_y - y1 * temp_x < x2 * temp_y - y2 * temp_x;
}

void solve()
{
    int n;
    cin >> n;
    //给数组p[]赋初值
    p.assign(n, {});
    //输入
    for(auto& [x, y] : p) cin >> x >> y;
    sort(p.begin(), p.end());
    //把点集的组合存入map中
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < n; j ++)
            if(i != j)
            {
                //p[i]的first赋值给xi, second赋值给yi
                auto[xi, yi] = p[i];
                auto[xj, yj] = p[j];
                // mp[向量] = {向量的初始位置}
                //存储的是向量相同,初始位置不同
                mp[{xj - xi, yj - yi}].emplace_back(xi, yi);
            }

    //记录最大面积
    ll ans = 0;
    //遍历map
    for(auto [pai_d, v] : mp)
    {
        //拿出向量的(x, y)
        temp_x = pai_d.first;
        temp_y = pai_d.second;

        //根据截距的大小进行从小到大排序
        sort(v.begin(), v.end(), cmp);

        //如果v的数量 >= 2, 就可以组成平行四边形
        if(v.size() != 1)
        {
            //找到截距最大值和最小值分别对应的向量起点坐标
            auto [x1, y1] = v[0];
            auto [x2, y2] = v.back();

            //求向量的外积,模长刚好为平行四边形的面积
            ll ps = (x2 * temp_y - y2 * temp_x) - (x1 * temp_y - y1 * temp_x);
            //取最大值
            ans = max(ans, ps);
        }
    }
    if(ans) cout << ans << ".0";
    else cout << "-1";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1;
    while(t --)
        solve();

    return 0;
}

F-小红小紫画线

思路:

  • 选四个点,作为两条线,因为在凸多边形上,所以四个点一定不共线。
    • 则一定有一种连接方式使得两条直线相交。
    • 因为两条直线可以交换,所以选了四个点后,一次有两种方案。
  1. 首先,求出所有C(sum, 4) 和 C(sum, 3),选四个点的组合数,选三个点的组合数之和。
  2. 去除不合法的方案:
    • 1.C(sum, 4)时,四个点在一条边上,C(sum, 3)时三个点在一条边上
    • 2.C(sum, 4)时,三个点在一条边上,C(sum, 3)时二个点在一条边上
      3.去除之后,就是合法方案数
#include<bits/stdc++.h>
using namespace std;

using ll = long long;
int mod = 1e9 + 7;

vector<ll> a;

//C(n, m) = n! / (n - m)! / (m)!
//求组合数
ll C(ll n, int m)
{
    ll p = 1;
    for(int i = 1; i <= m; i ++)
    {
        p = p * (n - i + 1) % mod;
        //防止有余数,除不尽。规避乘法逆元
        while(p % i) p += mod;
        p /= i;
    }
    return p;
}

void solve()
{
    int n;
    cin >> n;
    a.assign(n + 1, {});
    ll sum = 0;
    for(int i = 1; i <= n; i ++)
    {
        cin >> a[i];
        sum += a[i];
    }
    sum %= mod;
    //所有由四个点构成的两条线的组合, 所有由三个点构成的两条线的组合 相加
    ll ans = (C(sum, 4) * 2 + C(sum, 3) * 6) % mod;

    for(int i = 1; i <= n; i ++)
    {
        // 选4个点的情况下,三个点在一条边上是不允许的。选三个点的情况下,两个点在一条边上是不允许的。
        ll p = (C(a[i], 3) + C(a[i], 2) * 2) * 2 % mod * (sum + mod - a[i]) % mod;
        ans = (ans + mod - p) % mod;
        //选的点都在一条边上的方案数
        ll q = (C(a[i], 4) * 2 + (C(a[i], 3)) * 6) % mod;
        ans = (ans + mod - q) % mod;
    }
    cout << ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t = 1;
    while(t --)
        solve();

    return 0;
}```

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值