Codeforces Round #734 (Div. 3)补题

比赛时就做了前两个题跑路了,补一下剩下的

别骂了别骂了太菜了,C题做了半个小时TLE了,想了一会没啥优化思路就看了眼D然后去当我的情感大师去了,草

参考博客:1 2

B2. Wonderful Coloring - 2

传送门:B2. Wonderful Coloring - 2

看了别人的题解,这思路跟我大差不离啊,笨比一个不知道哪里出锅了,总之顺着别人的题解说一下

题意:给你n个方块以及方块上面的数字和k种颜色,让你为方块涂色,满足以下四个条件:
1、方块的涂色状态只有涂和不涂
2、相同颜色不能涂相同的数字
3、每种颜色使用次数一样
4、满足前三个条件下尽可能多的去涂色

1、先读入每个方块上的数字,并统计相同数字的个数,然后将数字次数超过 k 次的数字种超过的部分变成 0,比如,k = 3,方块种出现了四个 1,那么把最后一个 1 变成 0,这个时候 1 的个数不超过颜色的个数

2、开一个结构体,里面有方块的下标 i、方块上的数字和涂的颜色,再进行第一步操作后,就将结构体按照方块上的数字给整个结构体排序

3、排完序后,从方块上不是 0 的方块将进行染色,按照 1 ~ k 的顺序染色,并把颜色记录到对应方块的结构体中。这样涂色一定不会出现相同颜色涂相同数字,因为第一步操作和排序以后,后面如果有相同数字,一定是连续的且次数不会超过 k,所以一定满足第二个条件

4、我们求方块上的数字不是 0 的个数,这是准备涂色的个数,但是要满足第四个条件,我们对准备涂色的个数 % k ,假设取模为 mod,在排完序以后涂色涂到 n - mod 就不涂了,剩余的涂色为 0,即不涂了,这样能保证满足条件三,且同时满足条件四

代码:

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e7 + 10;
const int maxm = 1e4 + 10;

int t, n, k;
struct node{
  int pos, num, ans;
}a[200010];
 
inline int read() {
  int x = 0, k = 1; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') k = -1;
  for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
  return x * k;
}

inline bool cmp1(node x, node y) {
  return x.num < y.num;
}

inline bool cmp2(node x, node y) {
  return x.pos < y.pos;
}

int main() {
  t = read();
  while (t--) {
    map<int, int> mp;
    n = read(); k = read();
    int sum = 0;
    for (int i = 1; i <= n; i++) {
      a[i].num = read();
      a[i].pos = i;
      mp[a[i].num]++;
      sum++;
      if (mp[a[i].num] > k) {
  	a[i].num = 0;
  	mp[a[i].num]--;
  	sum--;
      }
    }
    sort(a + 1, a + 1 + n, cmp1);
    int mod = sum % k;
    int now = 1;
    for (int i = 1; i <= n - mod; i++) {
      if (a[i].num == 0) a[i].ans = 0;
      else {
  	a[i].ans = now;
  	now++;
  	if (now > k) now = 1;
      }
    }
    for (int i = n - mod + 1; i <= n; i++)
      a[i].ans = 0;
    sort(a + 1, a + 1 + n, cmp2);
    for (int i = 1; i <= n; i++) 
      printf("%d ", a[i].ans);
    printf("\n");
    for (int i = 1; i <= n; i++)
      a[i].ans = 0, a[i].pos = 0, a[i].num = 0;
  }
  return 0;
}

以及我的 TLE 了的代码,指不定哪天就良心发现了

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e7 + 10;
const int maxm = 1e4 + 10;

int t, n, m, cnt, ans[maxn];
struct node{
  int num, pos;
}a[maxn * 2];

inline int read() {
  int x = 0, k = 1; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') k = -1;
  for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
  return x * k;
}

inline bool cmp(node x, node y) {
  if (x.num == y.num) return x.pos < y.pos;
  return x.num < y.num;
}

int main() {
  t = read();
  while (t--) {
    memset(ans, 0, sizeof(ans));
    n = read(); m = read();
    int cntnum = 0;
    map<int, int> mp;
    cnt = 0;
    for (int i = 1; i <= n; i++) {
      a[i].num = read();
      a[i].pos = i;
      mp[a[i].num]++;
      if (mp[a[i].num] <= m) cnt++;
    }
    sort(a + 1, a + 1 + n, cmp);
    cnt /= m;
    int now = 1;
    for (int i = 1; i <= n; i++) {
      mp[a[i].num]--;
      if (mp[a[i].num] < m) {
      	ans[a[i].pos] = now;
      	now++;
      	if (now > m) now -= m;
      	cntnum++;
      }
      if (cntnum >= cnt * m) break;
    }
    for (int i = 1; i <= n; i++) 
      printf("%d ", ans[i]);
    printf("\n");
  }
  return 0;
}

C. Interesting Story

传送门:C. Interesting Story

本题题意:给你n个字符串,里面只包含a,b,c,d,e这五种字符,定义一个故事是有趣的:故事由给你的字符串中的任意个组成,要满足其中出现了一个字母的个数比其他字母出现的个数要多这个条件,问你在给你的n个字符串中如何挑选使故事变为有趣且用的字符串最多。

1、我们先把问题具体化:一个故事有趣即出现最多的字符个数的两倍减去字符的总个数要大于 0。即 ans = num * 2 - sum > 0 num 是出现最多的字母,sum 是字母的总个数

2、出现次数最多的字母有五种情况:每次讨论一个字母,求出所用字符串最多的个数,然后五个字母取个最大值。

3、贪心求最多的字符串个数,对于每一个字母讨论时, 先选 ans 最大的,然后再依次选最小的,直至选到下一个使所有的 ans 的和小于等于 0 时停止选,记录选的个数,这样对于每个字母而言在满足条件下选的一定是最多的字符串。

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e7 + 10;
const int maxm = 1e4 + 10;

int t, n;
vector<string> a;
vector<int> num;
char ch[5] = {'a', 'b', 'c', 'd', 'e'};
 
inline int read() {
  int x = 0, k = 1; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') k = -1;
  for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
  return x * k;
}

int main() {
  t = read();
  while (t--) {
    a.clear(); num.clear();
    n = read();
    while (n--) {
      string s;
      cin >> s;
      a.push_back(s);
    }
    int maxnum = 0, w = 0;
    for (int i = 0; i < 5; i++) {
      w = 0;
      num.clear();
      char c = ch[i];
      for (auto it : a) {
      	int cnt = 0;
      	for (int j = 0; j < it.length(); j++)
      	  if (it[j] == c) cnt++;
      	int ans = cnt * 2 - it.length();
      	num.push_back(ans);
      }
      sort(num.begin(), num.end(), greater<int>());
      int sum = 0;
      for (auto it : num) {
      	sum += it;
      	if (sum > 0) w++;
      }
      maxnum = max(w, maxnum);
    }
    printf("%d\n", maxnum);
  }
  return 0;
}

D1. Domino (easy version)

传送门:D1. Domino (easy version)

题意:t组测试数据,每组给你一个n * m的表(n*m一定为偶数),你可以用1 * 2或者2 * 1的多米诺骨牌填充它,问你可不可以用填满它且只用k个1 * 2的多米诺骨牌

我们一定会用 n * m / 2 个骨牌填满这个表, n * m 一定为偶数,那么 n 或者 m 最多有一个奇数,我们先看 n,m 都是偶数的情况下,在一个 2 * 2 的表中我们发现不管是横着填充还是竖着填充都可以,也就是说,此时只要k是偶数就一定满足(2 * 2的必须用成对的骨牌填充),n 为奇数的情况下,我们最下面多了一行,必须填 m/2 个横的骨牌,m 为奇数的情况下,最右边多一列,必须填 n / 2 个竖的骨牌把多出来的补上,剩下的就随便填充了

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e7 + 10;
const int maxm = 1e4 + 10;

int t, n, m, k;
 
inline int read() {
  int x = 0, k = 1; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') k = -1;
  for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
  return x * k;
}

int main() {
  t = read();
  while (t--) {
    n = read(); m = read(); k = read();
    int sum = n * m / 2;
    if (n % 2 == 1) {
      sum -= m / 2;
      k -= m / 2;
    }
    if (m % 2 == 1) sum -= n / 2;
    if (k < 0 || k % 2 == 1 || k > sum) 
      printf("NO\n");
    else printf("YES\n");
  }
  return 0;
}

D2. Domino (hard version)

传送门:D2. Domino (hard version)

题意:原则同上,但要用小写英文字母(随便什么字母)输出那个表,保证相邻的字母不一样就行

大体不变,减行减列时特殊处理,其他行列我们先把k个横着的填出来,剩下的全填竖着的即可,主要是相邻字母不一样,我们可以根据行列规律填就是了

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e7 + 10;
const int maxm = 1e4 + 10;

int t, n, m, k, sum;
char ans[maxm][maxm];

inline int read() {
  int x = 0, k = 1; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') k = -1;
  for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
  return x * k;
}

int main() {
  t = read();
  while (t--) {
    n = read(); m = read(); k = read();
    int nn = n, mm = m;
    for (int i = 1; i <= n; i++)
      for (int j = 1; j <= m; j++)
        ans[i][j] = '0';
    sum = n * m / 2;
    if (n % 2 == 1) {
      int cnt = 0;
      for (int i = 1; i <= m; i += 2) {
      	cnt++;
      	if (cnt % 2 == 1) ans[n][i] = ans[n][i + 1] = 'x';
      	else ans[n][i] = ans[n][i + 1] = 'y';
      }
      n--;
      k -= m / 2;
      sum -= m / 2;
    }
    if (m % 2 == 1) {
      int cnt = 0;
      for (int i = 1; i <= n; i += 2) {
      	cnt++;
      	if (cnt % 2 == 1) ans[i][m] = ans[i + 1][m] = 'i';
      	else ans[i][m] = ans[i + 1][m] = 'o';
      }
      m--;
      sum -= n / 2;
    }
    if (k < 0 || k % 2 == 1 || k > sum) {
      printf("NO\n");
      continue;
    }
    int cnt = 0;
    for (int j = 1; j <= m; j += 2) {
      cnt++;
      for (int i = 1; i <= n; i++) {
      	if (k == 0) break;
      	if ((cnt + j) % 2 == 1) ans[i][j] = ans[i][j + 1] = 'a';
      	else ans[i][j] = ans[i][j + 1] = 'b';
	k--;
	cnt++;
	if (k == 0) break; 
      }
      if (k == 0) break;
    }
    cnt = 0;
    for (int i = 1; i <= n; i += 2) {
      cnt++;
      for (int j = 1; j <= m; j++) 
        if (ans[i][j] == '0')
          if ((j + cnt) % 2 == 1) ans[i][j] = ans[i + 1][j] = 'c';
          else ans[i][j] = ans[i + 1][j] = 'd'; 
    }
    printf("YES\n");
    for (int i = 1; i <= nn; i++) {
      for (int j = 1; j <= mm; j++)
        printf("%c", ans[i][j]);
      printf("\n");
    }
  }
  return 0;
}

E. Fixed Points

传送门:E. Fixed Points

题意:t组测试数据,每组测试数据包含一个长度为n的数组和一个常数k
对数组有两种操作
1 对当前数字什么也不做
2 删掉当前数字,后面的数字全都往前移一位
定义一个数组b1,b2 … bm为原数组经过ans次操作2后的数组,其中bi=i 的元素有>=k个,问ans最小是几

dp题,我们定义一个二维数组 dp[i][j] 表示一共经过了 i 个元素,经行了 j 次操作 2,那么递推公式就出来了

  • 不删除 a[i],那么 dp[i][j] = dp[i-1][j-1]
  • 删除 a[i],那么 dp[i][j] = dp[i-1][j] + (a[i] == i-j)
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e7 + 10;
const int maxm = 2e3 + 10;

int t, n, k, a[maxm];
int dp[maxm][maxm];

inline int read() {
  int x = 0, k = 1; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') k = -1;
  for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
  return x * k;
}

int main() {
  t = read();
  while (t--) {
    n = read(); k = read();
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; i++)
      a[i] = read();
    for (int i = 1; i <= n; i++)
      for (int j = 0; j <= i; j++)
        if (j == 0) dp[i][j] = dp[i - 1][j] + (a[i] == i - j);
        else dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j] + (a[i] == i - j));
    bool flag = 0;
    for (int j = 0 ; j <= n ; j++) 
	  if (dp[n][j] >= k) {
        flag = 1;
        printf("%d\n",j);
        break ;
      }
    if (!flag) printf("-1\n");
  }
  return 0;
}
posted @ 2021-07-30 16:52  Moominn  阅读(90)  评论(0编辑  收藏  举报