Educational Codeforces Round 137 (Rated for Div. 2) (A-E)题解

比赛链接

A. Password

大水题,求个组合数就水掉了。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long

template <class T>
inline void read(T& a){
	T x = 0, s = 1;
	char c = getchar();
	while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
	a = x * s;
	return ;
}

vector <int> a; 
int fac[N];

void prepare(){
  fac[0] = fac[1] = 1; 
  for(int i = 1; i <= 10; i++)
    fac[i] = fac[i-1] * i;
  return ; 
}

int C(int a, int b){
  return fac[b] / (fac[a] * fac[b-a]); 
}

int main(){
  // freopen("hh.txt", "r", stdin); 
  int T; read(T);
  prepare(); 
  while(T--){
    // a.clear(); 
    int n; read(n);
    for(int i = 1; i <= n; i++){
      int x; read(x); 
    }

    cout << C(2, 10 - n) * 6 << endl; 

  }
  return 0;
}

B. Permutation Value

大水题。首先可以知道小的数字在一个排列中是不可缺少的。所以把小数字尽量放远一点即可。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long

template <class T>
inline void read(T& a){
	T x = 0, s = 1;
	char c = getchar();
	while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
	a = x * s;
	return ;
}

int ans[N]; 

int main(){
  // freopen("hh.txt", "r", stdin); 
  int T;read(T);
  while(T--){
    memset(ans, 0, sizeof(ans)); 
    int n; read(n);
    int tot = 0;
    int p = 1;
    int dis = 0; 
    for(int i = 1; i <= n; i++){
      ans[p] = i; 
      if(i % 2 == 0) p = dis + 1; 
      else p = n - dis, dis++; 
    }
    for(int i = 1; i <= n; i++){
      printf("%d ", ans[i]); 
    }
    cout << endl; 
  }
  return 0;
}

C. Save the Magazines

相对没那么水。设 \(dp\) 数组 \(dp_{i,0/1}\) 表示第 \(i\) 件物品为止,是否转移到前一个物品,情况下的最大值。公式见代码。注意讨论是否可以转移到前一个物品以及前提条件。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long

template <class T>
inline void read(T& a){
	T x = 0, s = 1;
	char c = getchar();
	while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
	a = x * s;
	return ;
}

int n;
char s[N]; 
int a[N]; 
int dp[N][2]; 

int main(){
  // freopen("hh.txt", "r", stdin); 
  int T; read(T);
  while(T--){
    // memset(dp, 0, sizeof(dp)); 
    read(n);
    scanf("%s", s + 1); 
    for(int i = 1; i <= n; i++) read(a[i]);

    for(int i = 1; i <= n; i++){
      if(s[i] == '1'){
        if(s[i-1] == '1') dp[i][1] = dp[i-1][1] + a[i-1]; 
        else dp[i][1] = dp[i-1][0] + a[i-1]; 
        dp[i][0] = max(dp[i-1][0], dp[i-1][1]) + a[i]; 
      }
      else{
        dp[i][1] = -1e9; 
        dp[i][0] = max(dp[i-1][0], dp[i-1][1]); 
      }
    }
    cout << max(dp[n][0], dp[n][1]) << endl; 
  }
  return 0;
}

D. Problem with Random Tests

本次比赛争议很大的一道题。由于全是随机数据,难度大大降低。首先容易想到,

可以贪心找到以 \(1\) 开头的一个长串,这样肯定可以保证结果尽量大。然后考虑寻找第二个串来跟他 \(or\) 一下。由于纯随机数据,复杂度期望 \(O(logN)\),很难卡掉,直接一个个暴力比较即可。

(这道题使用 string 会比较好写。本人比赛时太傻了,使用了数组。。。。)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long

template <class T>
inline void read(T& a){
	T x = 0, s = 1;
	char c = getchar();
	while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
	a = x * s;
	return ;
}

int n;
char t[N]; 
int s[N];
int tmp[N]; 
int ans[N];  
int m; 

bool check(int *a, int *b, int n){  // 检查 a 是否大于等于 b
  for(int i = 1; i <= n; i++){
    if(a[i] < b[i]) return 0; 
    if(a[i] > b[i]) return 1; 
    if(a[i] == b[i]) continue; 
  }
  return 1; 
}

int main(){
  // freopen("hh.txt", "r", stdin); 
  read(m);
  scanf("%s", t + 1);
  int p = -1; 
  for(int i = 1; i <= m; i++)
    if(t[i] == '1'){
      p = i;
      break; 
    }
  if(p == -1){
    cout << 0 << endl;
    return 0; 
  }
  n = m - p + 1; // 当前串的总长度
  int id = 0; 
  for(int i = p; i <= m; i++){
    s[++id] = t[i] - '0'; // 第一个串
  }

  for(int i = 1; i <= n; i++){
    if(s[i] == 0){
      p = i;
      break; 
    }
  }
  memcpy(ans, s, sizeof(ans)); 

  int len = n - p + 1;  // 小串的长度
  for(int i = 1; i + len <= n; i++){   // i: 小船的头
    memcpy(tmp, s, sizeof(tmp));   // 存一个副本
    if(s[i] != 1) continue; 
    for(int j = p, now = 0; j <= n && now < len; j++, now++){  
      tmp[j] |= s[i+now];  
    }
    if(check(tmp, ans, n)) memcpy(ans, tmp, sizeof(ans)); 
  }
  for(int i = 1; i <= n; i++)
    printf("%d", ans[i]); 
  //未完待续
  return 0;
}

E. FTL

本蒟蒻到 \(E\) 题便有些吃力了。考虑 \(dp\)。设 \(dp_x\) 表示造成 \(x\) 伤害所需要的最小时间。 考虑到若将剩余血量设为状态,可能出现负数,造成下标问题,故将其反向。

状态转移: 对于当前血量 \(x\),我们有三种情况:用一号大炮单独打一次,用二号大炮单独打一次,二者一起打一次(中间可能包扩各自打)。对于前两种情况,比较简单:

\(dp[x] = min(dp[x - (p1 - p)] + t1, dp[x - (p2 - p)] + t2\)

对于第三种情况,画图考虑:

对于 \(t_2\) 大于 \(t_1\) 的情况,如此考虑:两门炮先各自打若干炮,然后最后一起打一炮。这样就可以覆盖所有的情况了。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define N 1000010
#define ll long long

template <class T>
inline void read(T& a){
	T x = 0, s = 1;
	char c = getchar();
	while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); }
	a = x * s;
	return ;
}

ll dp[N]; 
ll p1, t1;
ll p2, t2; 
ll M, p; 

int main(){
  // freopen("hh.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);  
  read(p1), read(t1);
  read(p2), read(t2);
  read(M), read(p);

  memset(dp, 0x3f3f3f3f3f3f3f, sizeof(dp)); 
  dp[0] = 0; 

  for(int i = 1; i <= M; i++){
    dp[i] = min(dp[i], dp[max(0ll, i - p1 + p)] + t1); 
    dp[i] = min(dp[i], dp[max(0ll, i - p2 + p)] + t2); 
    for(int j = 1; j <= i; j++){
      if(j * t1 >= t2){
        ll tmp = (j - 1) * (p1 - p) + ((j * t1 - t2) / t2) * (p2 - p) + (p1 + p2 - p); 
        dp[i] = min(dp[i], dp[max(0ll, (ll)i-tmp)] + j * t1); 
      }
      if(j * t2 >= t1){
        ll tmp = (j - 1) * (p2 - p) + ((j * t2 - t1) / t1) * (p1 - p) + (p1 + p2 - p); 
        dp[i] = min(dp[i], dp[max(0ll, (ll)i-tmp)] + j * t2); 
      }
    }
  }

  cout << dp[M] << endl; 
  return 0;
}

F和G:不会

posted @ 2022-10-18 18:47  雪之下,树之旁  阅读(166)  评论(1编辑  收藏  举报