Codeforces 919 (div2) a-c题题解

本文详细解析了Codeforces919比赛中的三个问题(A-C):A题涉及简单模拟,B题是关于博弈策略优化,C题需要利用数论找到符合条件的整数划分。文章强调了模拟过程中避免超时的方法和博弈问题中策略的制定技巧。
摘要由CSDN通过智能技术生成

Codeforces 919 (div2) a-c题题解

A题 Satisfying Constraints Problem - A - Codeforces

题目概述:一共有3种约束情况。当a = 1时,将k下限约束到x;当a = 2时,将s上限约束到x,当a = 3时;当a = 3时,k将不能等于x;(k和x含义请看题目);

类型:简单模拟;

要点注意:

1.最后在数满足条件的个数cnt时,不能直接正着+1+1(否则会TLE),而是要把先把上下限中间的数通过减法算出来,然后通过-1-1排除掉不合规的点。

2.答案最小值为0,所以输出时有max(cnt,0ll);注意ll;

//
// Created by Mrlaolu on 2024/1/13.
//
#include<bits/stdc++.h>

#define int long long
#define endl '\n'
using namespace std;

const int N = 1010;

void solve()
{
  int n;
  cin >> n;
  int min1 = 0;
  int max1 = INT_MAX;
  vector<int>arr;
  for(int i = 0;i < n;++i)
  {
    int a,t;
    cin >> a >> t;
    if(a == 1)
    {
      min1 = max(min1,t);
    }
    else if(a == 2)
    {
      max1 = min(max1,t);
    }
    else if(a == 3)
    {
      arr.push_back(t);
    }
  }

  int cnt = max1 - min1 + 1;
  for(auto it : arr)            //-based for 遍历arr
  {
    if( it >= min1 && it <= max1){cnt--;}
  }
  cout << max(cnt,0ll) << endl;

}

signed main()
{

  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int t = 1;
  cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}

B题 Summation Game Problem - B - Codeforces

题目概述:alice先手在数组中移除k个数据,rob后手在数组中将k个数据乘以-1(变为自己的倒数)。alice希望将操作后数组的总和最大化,rob希望将操作后数组的总和最小化。

类型:博弈题,找规律。

要点注意:

1.因为是alice先手,所以最后总和肯定是要最大化的(相当于我们是alice),而在alice处理后rob也要想方设法让alice处理后的数组总和最小。

2.通过观察可以发现,alice如果要移除数据也是要移除数组里最大的那几个(因为rob只要拿了当前数组中最大的那个就能让总和最小化,换句话说,拿rob对他最有利),所以可以用sort()对数组进行排序,然后进行从大到小进行移除,每次移除都算总和,最后通过查找出最大的总和来作为答案。

3.在从大到小进行移除并算总和时,注意不能正向一个个加过来(会变成O(N^2)爆时间复杂度),而是要倒着维护总和sum(详情看代码)。

//
// Created by Mrlaolu on 2024/1/13.
//
#include<bits/stdc++.h>

#define int long long
#define endl '\n'
using namespace std;

const int N = 1010;

void solve()
{
  int n,k,x;
  cin >> n >> k >> x;

  vector<int>a(n);
  int sum = 0;
  int seg = 0;


  for(int i = 0; i < n; ++i)
  {
    cin >> a[i];
    sum += a[i];
  }

  sort(a.begin(),a.end(),greater<int>());
  for(int i = 0;i < x;++i)
  {
    seg += a[i];
  }
  int sumr = 0;
  vector<int>arr(k + 1);

  for(int i = 0;i <= k;++i)          //维护sum部分 这样子时间复杂度是O(n^2)
  {
    arr[i] = sum - 2 * seg - sumr;
    seg -= a[i];
    sumr += a[i];
    if(i + x < n){seg += a[i + x];}
  }

  int ans = -1e9;
  for(int i = 0;i < k + 1;++i)
  {
    ans = max(ans,arr[i]);
  }

  cout << ans << endl;


}
signed main()
{

  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int t = 1;
  cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}

C题 Partitioning the Array Problem - C - Codeforces

题目概述:穷举出所有k使得k能被n整除,并将数组平均切成n / k份(每份长度为k),如果存在一个m(m >= 2)使得将所有数的每个数都变为自身模m( a i a_i ai = a i a_i ai % m)后,各份数组相互全等(完全),则k可行。求有多少个可行的k。

类型:暴力,数论。

注意要点:

1.最重要的点就在于如何判断有没有m使这个k成立,而不是获取这个准确的m。而这个m成立的关键就在于——在每份数组中相对位置相同的数据余数相同(也就是说 a i − m ∗ b = = a i + k − m ∗ c a_i - m * b == a_{i+k} - m * c aimb==ai+kmc ,其中b,c为随机一个符合条件的值)。所以,我们可以通过将在每份数组中处于相同的相对位置的数据进行做差,并通过求gcd,如果gcd(最大公约数)不为1,则说明这个最大公约数就是m,则这个k可行。

//
// Created by Mrlaolu on 2024/1/13.
//
#include<iostream>
#include "vector"
#include "map"
#include "algorithm"

#define int long long
#define endl '\n'
using namespace std;

const int N = 1010;

void solve()
{
  int n;
  cin >> n;
  vector<int>a(n);

  for (int i = 0; i < n; ++i)
    cin >> a[i];

  vector<int>div;

  int cnt = 0;

  for (int i = 1; i < n; ++i)
  {
    if (!(n % i)) { div.push_back(i); }
  }

  for (int i = 0; i < div.size(); ++i)
  {
    vector<int>dif;
    int g = -1;

    for (int p = 0; p + div[i] < n; ++p)
    {
      int temp = abs(a[p] - a[p + div[i]]);
      if (!temp) { continue; }
      dif.push_back(temp);
      if(g == -1){g = temp;}
      else {g = __gcd(g,temp);}
    }
    if (g != 1) { cnt++; }

  }
  cout << cnt + 1 << endl;

}

signed main()
{

  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  int t = 1;
  cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值