1. 程式人生 > 其它 >Codeforces Round #734 (Div. 3)補題

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;
}