zoukankan      html  css  js  c++  java
  • 牛客小白月赛14 :部分题目总结

     题目链接:https://ac.nowcoder.com/acm/contest/879#question

     官方题解:https://ac.nowcoder.com/discuss/189446?type=101&order=0&pos=1&page=1

    A .简单计数

    sol:考虑两个数组a和b,a[i]表示第i天在1号城市的方案数,b[i]表示第i天不在1号城市的方案数,可以得到如下dp方程式:

    a[i] = b[i - 1];    b[i] = a[i - 1] * (n - 1) + b[i - 1] * (n - 2);

    a[k]就是问题的答案。但是k比较大,不能暴力循环。可以将dp方程式转换成矩阵来利用矩阵快速幂求解

    ps:n < 2的情况下上面的dp方程式就不对了。但是按照题目给出的maker.cpp,n和k的范围都在8e8到9e8之间;所以我就不得不吐槽一下这个示例1了,根本不满足数据范围。

    • 矩阵快速幂
      #include "bits/stdc++.h"
      using namespace std;
      const int MOD = 998244353;
      struct Mat {
          int mat[2][2];
          Mat() {memset(mat, 0, sizeof(mat));}
          friend Mat operator * (Mat a, Mat b) {
              Mat ans;
              for (int i = 0; i < 2; i++)
              for (int j = 0; j < 2; j++)
              for (int k = 0; k < 2; k++)
              ans.mat[i][j] = (ans.mat[i][j] + 1LL * a.mat[i][k] * b.mat[k][j]) % MOD;
              return ans;
          }
      };
      Mat mat_pow(Mat m, int k) {
          Mat ans;
          ans.mat[0][0] = ans.mat[1][1] = 1;
          while (k) {
              if (k & 1) ans = ans * m;
              m = m * m;
              k >>= 1;
          }
          return ans;
      }
      int main() {
          int n, k;
          scanf("%d%d", &n, &k);
          Mat m;
          m.mat[0][1] = 1;
          m.mat[1][0] = n - 1;
          m.mat[1][1] = n - 2;
          m = mat_pow(m, k);
          printf("%d
      ", m.mat[0][0]);
          return 0;
      } 
      View Code

    B .投硬币

     sol:把k次成功到n次成功的概率算出来,然后累加

    • 逆元
      #include "bits/stdc++.h"
      using namespace std;
      const int MAXN = 1e5 + 5;
      const int MOD = 998244353;
      int powa[MAXN], powb[MAXN], inv[MAXN]; 
      int ans, fac;
      int quick_pow(int n, int k) {
          int ans = 1;
          while (k) {
              if (k & 1) ans = 1LL * ans * n % MOD;
              n = 1LL * n * n % MOD;
              k >>= 1;
          }
          return ans;
      }
      int main() {
          // a 是成功的概率, b 是失败的概率 
          int n, k, a, b;
          scanf("%d%d%d", &n, &k, &a);
          b = (MOD + 1 - a) % MOD;
          powa[0] = powb[0] = fac = inv[0] = 1;
          for (int i = 1; i <= n; i++) {
              powa[i] = 1LL * a * powa[i - 1] % MOD;
              powb[i] = 1LL * b * powb[i - 1] % MOD;
              fac = 1LL * fac * i % MOD; 
          }
          inv[n] = quick_pow(fac, MOD - 2);
          // 线性地推阶乘逆元 
          for (int i = n - 1; i > 0; i--) inv[i] = 1LL * inv[i + 1] * (i + 1) % MOD;
          for (int i = k; i <= n; i++) {
              int p = 1LL * powa[i] * powb[n - i] % MOD;
              // 这个c就是排列组合数,C(n, i); 
              int c = 1LL * fac * inv[n - i] % MOD * inv[i] % MOD;
              ans = (ans + 1LL * p * c) % MOD;
          }
          printf("%d
      ", ans);
          return 0;
      }
      View Code

      第一次在比赛中运用逆元解出题目,而且比赛结束后又优化了代码,成功提交了目前为止本题最快代码。学习了线性递推阶乘的逆元。

    C .植树造林

      sol:到所有树的最远距离最小的树一定是最中间的树。所以,如果n为奇数,最中间的树有一棵,否则有两棵。

    • 贪心水题
      #include "bits/stdc++.h"
      using namespace std;
      int main() {
          int n;
          scanf("%d", &n);
          puts(n & 1 ? "1" : "2");
          return 0;
      }
      View Code

    D .签到提I

    sol:排个序就完事了

    • 排序
      #include "bits/stdc++.h"
      using namespace std;
      const int MAXN = 1e5 + 5;
      int arr[MAXN];
      int main() {
          int n, k;
          scanf("%d%d", &n, &k);
          for (int i = 1; i <= n; i++)
              scanf("%d", &arr[i]);
          sort(arr + 1, arr + 1 + n);
          printf("%d
      ", arr[k]);
          return 0;
      }
      View Code

      作为签到题,这题确实很水。不过如果把n的范围加到1e7,那么普通sort就要超时了。可以用堆排或手写的针对第k小的快排来完成。

    G .many sum

    sol:模拟素数筛的方法来求出b数组,然后把b数组异或起来得到答案

    • 素数筛的思路
      #include "bits/stdc++.h"
      using namespace std;
      const int MAXN = 2e6 + 5;
      int a[MAXN], b[MAXN];
      int n, MOD, ans;
      int main() {
          scanf("%d%d%d", &n, &a[1], &MOD);
          for (int i = 1; i <= n; i++) {
              for (int j = i; j <= n; j += i)
                  b[j] = b[j] + a[i];
              a[i + 1] = (a[i] + 7 * i + 7) % MOD;
              ans ^= b[i];
          }
          printf("%d
      ", ans);
          return 0;
      }
      View Code

      AB两道难的我做出来了,这题简单的比赛还没做出来。完全不知道d|i是什么意思,后来素数筛又算错了复杂度以为要超时。还是要仔细啊。

  • 相关阅读:
    kettle 9.1 windows 安装
    C# post 方式调用 webservice
    crm 2016 隐藏 crmTopBar 新建活动 新建记录 导入数据
    SqlServer修改数据库文件存放位置
    always on 下移动数据库文件 AlwaysON move database without breaking HADR
    kettle 启动问题 spoon 处理方法
    kettle Spoon.bat 启动 java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
    SQL 查询学生缺考情况
    SQL学习笔记:选取第N条记录
    观察者模式在MVP中的应用
  • 原文地址:https://www.cnblogs.com/Angel-Demon/p/10863900.html
Copyright © 2011-2022 走看看