Codeforces Round #697 (Div. 3)部分题解

Codeforces Round #697 (Div. 3)

C. Ball in Berland

思路: 思维

该题的题意是在所有的男女舞伴中选取两对,其中男舞伴和女舞伴均只能出现在一对中,问这样的取法总共有多少种。

首先所有的对数不可能重复,即同一对不可能出现两次。因此我们可以记录所有对数中每个人出现的次数。
然后遍历每一对,对于每一对的男女舞伴,只需在总对数中减去 男舞伴出现的次数 和 女舞伴出现的次数再加1(男女舞伴多减了一次),就是能和这对舞伴匹配的所有方案数,最后的答案还是除以2,因为重复计算。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>

#define maxn 200005
using namespace std;

struct Pair
{
  int Boy;
  int Girl;
}a[maxn];
int boy[maxn];
int girl[maxn];
void solve()
{
  int A, B ,k;
  cin >> A >> B >> k;
  for(int i = 0; i <= max(A, B); i++) boy[i] = girl[i] = 0;

  for(int i = 1; i <= k; i++)
  {
    cin >> a[i].Boy;
    boy[a[i].Boy]++;
  }
  for(int i = 1; i <= k; i++)
  {
    cin >> a[i].Girl;
    girl[a[i].Girl]++;
  }
  long long sum = 0;
  for(int i = 1; i <= k; i++)
  {
    sum += ( k - (boy[a[i].Boy] + girl[a[i].Girl] - 1));
  }
  cout << sum/2 << endl;
}

int main()
{
  int t = 1;
  scanf("%d", &t);
  while(t--)
    solve();

  return 0;
}

D. Cleaning the Phone

思路:二分

这题的题意是有一堆的app,每个app有一个占用的内存和重要程度,现在要求清理若干个app,使得清理的内存至少有M,且重要程度的总和最小。

刚开始的思路是贪心,但贪心其实在处理的过程中很多细节不能直接处理,需要打补丁。

观察重要程度可以发现总共只有两种重要程度,因此马上可以想到一个O(nlogn)的做法:将两种重要程度用两个数组来统计,然后每次数组从小到大排序后,再求他们的前缀和,接下来我们可以遍历重要程度的为1的数组,每次取完重要程度为1的app后,因为前缀和已经计算,可以马上求出当前已经清理的内存cur, 那么接下里只用在重要程度为2的app中直接二分查找到至少为 m-cur 的内存的左边边界即可。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define maxn 200005
using namespace std;
 
long long a[maxn];
int binary_Search(vector<long long> &a, long long k)
{
  int left = 0;
  int right = a.size();
  while(left < right)
  {
    int mid = (left + right) / 2;
    if(a[mid] >= k) right = mid;
    else if(a[mid] < k) left = mid+1;
 
  }
  return left;
}
void solve()
{
  long long n, m;
  scanf("%lld%lld", &n ,&m);
  vector<long long> a1, a2;
  for(int i = 1; i <= n; i++)
    scanf("%lld", &a[i]);
  for(int i = 1; i <= n; i++)
  {
    int x;
    scanf("%d", &x);
    if(x == 1) a1.push_back(a[i]);
    else a2.push_back(a[i]);
  }
  sort(a1.rbegin(), a1.rend());
  sort(a2.rbegin(), a2.rend());
 
  for(int i = 1; i < a1.size(); i++) a1[i] = a1[i] + a1[i-1];
  for(int i = 1; i < a2.size(); i++) a2[i] = a2[i] + a2[i-1];
 
  long long nowai = MAXN;
  long long nowbi = 0;
 
 
  long long index = binary_Search(a1, m);
  if(index != a1.size()) nowai = min(nowai, (index + 1));
 
  for(long long i = 0; i < a2.size(); i++)
  {
    if(a2[i] >= m)
    {
      nowai = min(nowai, (i+1)*2);
      break;
    }
    int index = binary_Search(a1, m-a2[i]);
    //cout << "index = " << index << endl;
    if(index != a1.size()) nowai = min(nowai, (i+1)*2 + (index + 1));
    //cout << " nowai = " << nowai << endl;
  }
  if(nowai == MAXN) cout << "-1" << endl;
  else cout << nowai << endl;
 
}
 
int main()
{
  int t = 1;
  scanf("%d", &t);
  while(t--)
    solve();
 
  return 0;
}

E. Advertising Agency

思路:杨辉三角

这题的题意是有n个博主,每个博主i有ai个粉丝,现在要求挑选k个博主,使得挑选的博主的粉丝数总和最多。

显然我们需要记录相同粉丝数的博主个数,然后从大到小挑选博主。假设当前最多粉丝数的博主有cnt个,如果cnt > k, 显然需要有从cnt中挑选k个博主的所有方案;如果cnt <= k, 则只有一种方案数,即全选,然后 k = k - cnt。

这里从cnt中挑选k个博主的所有方案显然是个组合数,可以用杨辉三角来计算。
杨辉三角的递推式为 mat[i][j] = (mat[i-1][j-1] + mat[i-1][j]) ,需要注意每次都要取余。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define maxn 1005
#define mod 1000000007
using namespace std;
 
long long mat[maxn][maxn];
long long combinat(int m, int n) {
    int i, j;
 
    if(n == 0 || m == n)
        return 1;
    for(j = 0; j <= n; j++) 
    { // 只要计算n列就行了,不用计算后面的
        mat[j][j] = 1;
        for(i = j+1; i <= m; i++) 
        {
            if(j == 0)
                mat[i][j] = 1;
            else
                mat[i][j] = (mat[i-1][j-1]%mod + mat[i-1][j]%mod) % mod;
        } // 计算Cmn
    }
    return mat[m][n];
}
int cnt[maxn];
void solve()
{
  int n, k;
  cin >> n >> k;
  for(int i = 1; i < maxn; i++) cnt[i] = 0;
  for(int i = 1; i <= n; i++)
  {
    int x;
    cin >> x;
    cnt[x]++;
  }
  long long ans = 1;
  int index = 1000;
  while(1)
  {
    while(cnt[index] == 0) index--;
    if(index <= 0 || k == 0) break;
 
    if(cnt[index] > k)
    {
      ans = (ans%mod * combinat(cnt[index], k)%mod) % mod;
      k = 0;
    }
    else  k -= cnt[index];
    index--;
  }
  cout << ans << endl;
}
 
int main()
{

  int t = 1;
  scanf("%d", &t);
  while(t--)
    solve();
 
  return 0;
}

F. Unusual Matrix

思路: 思维

这题的题意是给定一个矩阵,每次只能对矩阵的某一行或这某一列进行异或操作,然后问能不能在经过一些操作之后将该矩阵变成目标矩阵。

首先得发现一些特点。

(1) 异或操作就是将1变成0, 将0变成1。
(2) 对于某一行或者某一列的异或操作,实质上只能操作一次,操作两次的时候就是变回初始的矩阵,操作三次的时候就是操作一次的矩阵。
(3) 矩阵的行列操作没有先后的顺序关系。

因此我们考虑矩阵的第一行,遍历每个元素,如果该元素与目标矩阵不同,那么显然要操作一次,这里进行一次列操作,最后遍历完之后,第一行的元素一定和目标矩阵的第一行完全相同。

接下来考虑第二行, 遍历每个元素,如果该元素与目标矩阵不同,那么显然要操作一次,由于第一行的所有元素已经完全相同了,所以每行如果存在与目标矩阵不同的元素时,只能进行行操作了。

按这样的方式检查每一行即可。
需要注意的是,这里还缺少一种情况,因为第一行的元素与目标矩阵正好完全不同的时候,可以事先将第一行进行异或操作一次

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define maxn 1005
using namespace std;
 
int n;
bool check(vector<vector<int> > a, vector<vector<int> > &b)
{
  for(int i = 0; i < n; i++)
  {
    if(a[0][i] != b[0][i])
    {
      for(int j = 0; j < n; j++) a[j][i] ^= 1;
    }
  }
 
  for(int i = 0; i < n; i++)
  {
    int Xor = (a[i][0] ^ b[i][0]);
    for(int j = 1; j < n; j++)
    {
      if(Xor != (a[i][j] ^ b[i][j]))
        return false;
    }
  }
  return true;
}
void solve()
{
  cin >> n;
  string s;
  vector<vector<int> > a(n), b(n);
  for(int i = 0; i < n; i++)
  {
    cin >> s;
    for(int j = 0; j < n; j++) a[i].push_back(s[j] - '0');
  }
  for(int i = 0; i < n; i++)
  {
    cin >> s;
    for(int j = 0; j < n; j++) b[i].push_back(s[j] - '0');
  }
 
  for(int times = 1; times <= 2; times++)
  {
    if(check(a, b))
    {
      cout << "YES" << endl;
      return;
    }
    for(int i = 0; i < n; i++)
    {
      a[0][i] ^= 1;
    }
  }
  cout << "NO" << endl;
}
 
int main()
{
 
  int t = 1;
  cin >> t;
  while(t--)
    solve();
 
  return 0;
}

G. Strange Beauty

思路:思维

这题的题意是: 一个序列是美丽的序列当且仅当对于序列的中的任意一个数ai, 都有序列的中的其他任何数aj(i != j), 使得 ai | aj 或者 aj | ai。现在给定一个序列,问至少去掉多少个数之后可以成为美丽的序列。

首先我们应该从小到大处理这个序列,这样可以只考虑ai | aj 或者 aj | ai 中的一种情况,即对于一个数ai, 那么它一定是 2 * ai, 3 * ai, … n * ai 的因数。

为此我们需要记录序列中每个数出现过的次数, 然后从小到大遍历这个序列的时候 , 对于每个数,用logn的时间去更新它的倍数所能包含的因数个数。

最后取包含最大因数个数的值,然后用n减去这个数即可。、

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define maxn 200005
using namespace std;

int cnt[maxn], res[maxn];
void solve()
{
  int n;
  cin >> n;
  int maxv = 0;
  for(int i = 1; i < maxn; i++) cnt[i] = res[i] = 0;
  for(int i = 1; i <= n; i++)
  {
    int x;
    cin >> x;
    maxv = max(maxv, x);
    cnt[x]++;
  }

  for(int i = 1; i <= maxv; i++)
  {
    if(!cnt[i]) continue;

    res[i] += cnt[i];
    for(int j = 2*i; j <= maxv; j += i)
      res[j] = max(res[j], res[i]);
  }

  int ans = 0;
  for(int i = 1; i <= maxv; i++)
    ans = max(ans, res[i]);

  cout << n - ans << endl;
}

int main()
{
  int t = 1;
  cin >> t;
  while(t--)
    solve();

  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值