AtCoder Beginner Contest 282 A-E

比赛名称:HHKB Programming Contest 2022 Winter(AtCoder Beginner Contest 282)

比赛链接:AtCoder Beginner Contest 282

 A - Generalized ABC

给定字符串长度k,输出该字符串,该字符串由A,B,C...拼接得到 。

#include 
#define int long long
using namespace std;
 
signed main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int k;
  cin >> k;
  for(char i = 'A'; i <= 'Z'; i++){
    cout << i;
    k--;
    if(!k) break;
  }
  return 0;
}

B - Let's Get a Perfect Score 

给一个只有‘o''x'的字符矩阵,选两行,每一列至少一个’o',问有多少种方法

#include 
using namespace std;
 
signed main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n, m;
  cin >> n >> m;
  vector a(n);
  for(auto &ai:a) cin >> ai;
  int ans = 0;
  for(int i = 0; i + 1 < n; i++){
    for(int j = i + 1; j < n; j++){
      bool flag = 1;
      for(int k = 0; k < m; k++){
        if(a[i][k] == 'o' || a[j][k] == 'o'){
        }else{
          flag = 0;
          break;
        }
      }
      if(flag) ans++;
    }
  }
  cout << ans << "\n";
  return 0;
}

 C - String Delimiter

" "外的','替换为'.'

这里要注意转义字符‘\"' = "

#include 
using namespace std;
signed main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n;
  cin >> n;
  string s;
  cin >> s;
  bool flag = 0;
  for(char &c:s){
    if(c == '\"') flag ^= 1; 
    if(c == ',' && !flag) c = '.';
  }
  cout << s << "\n";
  return 0;
}

 D - Make Bipartite 2

题意:给定一张n个点的无向图,给其加一条边,满足其是二分图。问满足该条件的方案数

思路:

首先看二分图的概念:

就是顶点集可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集,两个子集内的顶点不相邻。通俗地讲任何无回路的图均是二分图

由于题目该图不一定是连通图,所以连通分量可能只有一个或多个,所以对此进行分类讨论:

只有一个连通分量:

该连通分量是否为一个二分图

  • 是一个二分图,这时候只要我们在两个点集之间任意加条边即可,不会影响其二分图的性质,所以方案数有cnt1*cnt2(即两个点集的数量相乘)
  • 不是一个二分图,如果不是一个二分图,无论怎么加边,都无法满足条件

有多个连通分量:

由上我们同样易证,只要任何一个连通分量不满足二分图,那么就无法满足条件

所以我们需要判断所有的连通分量是否都满足二分图性质

那么该条件下会贡献多少方案数呢?

答案是cnt1 * cnt2 + (cnt1 + cnt2) * (n - cnt1 - cnt2)

cnt1*cnt2上面已经证明过了

至于怎么得到(cnt1 + cnt2) * (n - cnt1 - cnt2)

我们可以发现由于所有的连通分量都是二分图,所以在不同的连通分量之间建边不会影响整体的二分图性质。

 我们知道ans2是计算连通分量之间建边的方案数,所以同一条边会被重复计算两遍,所以最后要/2-总边数m

AcCode:

#include 
#define int long long
using namespace std;
const int N = 2 * 1e5 + 6, M = 1e6 + 9;
int h[N], e[M], ne[M], idx;
int col[N];
int cnt1, cnt2;
void add(int a, int b){
  e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool dfs(int u, int c){
  col[u] = c;
  cnt1 += c == 0;
  cnt2 += c == 1;
  for(int i = h[u]; ~i; i = ne[i]){
    int j = e[i];
    if(col[j] == -1){
      if(!dfs(j, !c)) return false;
    }else if(col[j] == c) return false;
  }
  return true;
}
 
signed main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int n, m;
  cin >> n >> m;
  memset(h, -1, sizeof h);
  memset(col, -1, sizeof col);
  int d = m;
  while(m--){
    int a, b;
    cin >> a >> b;
    add(a, b), add(b, a);
  }
  int ans1 = 0, ans2 = 0;
  for(int i = 1; i <= n; i++){
    if(col[i] == -1){
      cnt1 = 0, cnt2 = 0;
      if(!dfs(i, 0)){
        cout << 0 << "\n";
        return 0;
      }
      ans1 += cnt1 * cnt2;
      ans2 += (cnt1 + cnt2) * (n - cnt1 - cnt2);
    }
  }
  cout << ans1 + ans2 / 2 - d;
  return 0;
}

 E - Choose Two and Eat One

题意:

给定n个数,每个数范围[1,m−1],每次选择两个数x,y,获得 x^y+y^xmodm 分数。再将其中一个数丢弃,直至剩一个数。问获得的分数最大值。

思路:

刷题太少的缘故吧看了老半天也没看出来是啥题型,最后模拟一遍发现是个最大生成树模型。

怎么抽象出来呢?

我们可以发现在多轮的情况下,每个数可能被选多次,但是丢弃的话只要一次,一对多的关系很像树节点。

那么抽象出来就是每次删除叶子节点,获得叶子与父节点的边权值。

所以本题解题过程就是:建边,建图,跑最大生成树

AcCode

#include 
using LL = long long;
using namespace std;
const int N = 550;
int p[N], a[N];
int n, m;
struct node{
  int a, b, w;
}edge[N * N];
int qpow(int a, int b){
  int ans = 1;
  while(b){
    if(b & 1) ans = (LL)ans * a % m;
    b >>= 1;
    a = (LL)a * a % m;
  }
  return ans;
}
 
bool cmp(node i, node j){
  return i.w > j.w;
}
 
int find(int x){
  if(p[x] == x) return x;
  return p[x] = find(p[x]);
}
 
int f(int x, int y){
  return (qpow(x, y) + qpow(y, x)) % m;
}
 
int main(){
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  cin >> n >> m;
  for(int i = 1; i <= n; i++) cin >> a[i];
  int cnt = 0;
  for(int i = 1; i <= n; i++){
    for(int j = i + 1; j <= n; j++){
      edge[++cnt] = {i, j, f(a[i], a[j])};
    }
  }
  for(int i = 1; i <= n; i++) p[i] = i;
  sort(edge + 1, edge + 1 + cnt, cmp);
  LL ans = 0;
  for(int i = 1; i <= cnt; i++){
    int a = edge[i].a, b = edge[i].b, w = edge[i].w;
    a = find(a), b = find(b);
    if(a != b){
      p[a] = b;
      ans += w;
    }
  }
  cout << ans << "\n";
  return 0;
}

 另外本题个点很迷惑,欢迎评论区大佬指正

find函数最开始这么写,一直出现自测RE

int find(int x){
  return (p[x] == x ? x : (p[x] == find(p[x])));
}

改成下面这个形式就直接AC了....emmmm...仍百思不得其解 

int find(int x){
  if(p[x] == x) return x;
  return p[x] = find(p[x]);
}

你可能感兴趣的:(每日刷题打卡,AtCoder,算法,c++,AtCoder)