• 寒假补题


    H-牛牛看云_2022牛客寒假算法基础集训营1 (nowcoder.com)

      思路:

    赛时和队友都想到了二分的做法,但是我觉得这个数据量1000 * 1000完全可以过啊,于是赛后就看到了下面这个代码,这个代码的思维量比二分大不少

    既然要求所有任意数对与1000差值的绝对值的和,每个数的范围又是 0 - 1000,统计个数就行

    b.collections 相当于是把 a 里的出现的每个 x 放到 map 里使得:map[x]++,得到的 map 就是 b

    第一个双重循环:对于每个数值相异数对,拿一个 i 有 b[i] 种取法,拿一个 j 有 b[j] 种取法,公式考虑顺序,因此除以排列数A22,也就是2

    第二个单重循环:对于数值不相异的数对,如果位置相异,当作数值相异计算,因此包含在二重循环中,位置相同则是自己和自己的贡献

    此时不需要用 b 单独算组合数,直接扫一遍 a 数组就行

      代码:

     1 import collections
     2 n = int(input())
     3 a = list(map(int, input().split()))
     4 b = collections.Counter(a)
     5 ans = 0
     6 for i in range(1001):
     7     for j in range(1001):
     8         ans += b[i] * b[j] * abs(i + j - 1000) / 2
     9 for i in range(n):
    10     ans += abs(a[i] - 500)
    11 print(int(ans))

    F-小沙的算数_2022牛客寒假算法基础集训营2 (nowcoder.com)

      思路:

    乘法块分出来之后wa了,问题出在题目数据太大而进行了取模运算,取模和除法的顺序不能换

    因此用逆元,但是我队当时三个人都wa了,逆元好像用的不对。。。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int mod = 1e9 + 7;
     5 const int N = 1e6 + 8;
     6 char s[N];
     7 ll num[N], pos[N], n, q, a[N];
     8 ll ksm(ll a, ll b) {
     9     ll ans = 1;
    10     while (b) {
    11         if (b & 1) (ans *= a) %= mod;
    12         (a *= a) %= mod;
    13         b >>= 1;
    14     }
    15     return ans;
    16 }
    17 int main() {
    18     cin >> n >> q;
    19     scanf("%s", s + 1);
    20     for (int i = 1; i <= n; i++) cin >> a[i];
    21     int cnt = 1;
    22     num[1] = a[1];
    23     pos[1] = 1;
    24     //放到块里
    25     for (int i = 2; i <= n; i++) {
    26         if (s[i - 1] == '*') {
    27             pos[i] = cnt;
    28             num[cnt] = num[cnt] * a[i] % mod;
    29         }
    30         else {
    31             num[++cnt] = a[i];
    32             pos[i] = cnt;
    33         }
    34     }
    35     ll ans = 0, x = 0, y = 0;
    36     for (int i = 1; i <= cnt; i++)  (ans += num[i]) %= mod;
    37 
    38     while (q--) {
    39         cin >> x >> y;
    40         //先把答案里去掉这块
    41         ans = (ans - num[pos[x]] + mod) % mod;
    42         //更新这块,然后加回去就完事了
    43         //原块的值 * 修改值 % mod / 原值 % mod 错到西伯利亚去了
    44         //原块的值 * 原值的逆元 % mod * 修改值 % mod
    45         //就记住,乘上 k 的逆元相当于除 k 就完事了
    46         num[pos[x]] = num[pos[x]] * ksm(a[x], mod - 2) % mod * y % mod;
    47         ans = (ans + num[pos[x]]) % mod;
    48         a[x] = y;
    49         cout << ans << endl;
    50     }
    51     return 0;
    52 }

    A-小沙的炉石_2022牛客寒假算法基础集训营2 (nowcoder.com)

    思路:

    想到了伤害可以通过调整牌的顺序改变,应该是一个区间,但是没想到是二分攻击次数

    不过复杂度最低的解法其实也不是二分,直接判断就得了

    使用所有攻击牌最低伤害是:+sp,atk,+sp,atk,...

    这样的伤害是1 + 3 + 5 + ... + min(n, m)  = min(n, m) ** 2是个奇数等差数列

    最大伤害是:+sp,+sp,... ,atk,atk,atk...

    这样伤害是 (m + 1) + (m + 2) + ... + (m + n) = (2 * m + n + 1) * n / 2

    代码:

     1 n,m=map(int,input().split())
     2 a=(min(n,m)+1)**2-1;
     3 b=min(n,m+1)
     4 c=(2*m+1+b)*b//2
     5 t=int(input())
     6 while t:
     7     t-=1
     8     x=int(input())
     9     if x==a or x>c:
    10         print("NO")
    11     else:
    12         print("YES")

    I-小沙的构造_2022牛客寒假算法基础集训营2 (nowcoder.com)

    思路:看似字符串,其实模拟题

    把字符分为两类:第一类自身对称,见代码 s1,第二类相互对称,见 s2 和 s3

    然后贪心的模拟就ok了

    代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n, m;
     4 char ans[1000005];
     5 string s1 = "\"!'*+-.08:=^_WTYUIOAHXVM|";
     6 string s2 = "<\\[{(";
     7 string s3 = ">/]})";
     8 int main() {
     9     cin >> n >> m;
    10     if (m > n || m > 35) {
    11         cout << -1 << endl;
    12         return 0;
    13     }
    14     int l = 1, r = n;
    15     int p = 0, q = 0;
    16     while (m > 2 || (m == 2 && l + 1 == r)) {
    17         cout << "p: " << p << " ans: " << ans + 1 << endl;
    18         if (p >= 5) {
    19             break;
    20         }
    21         ans[l++] = s2[p];
    22         ans[r--] = s3[p];
    23         m -= 2;
    24         p++;
    25     }
    26     while (l <= r) {
    27         if (m == 0) {
    28             ans[l++] = ans[r--] = s1[q - 1];
    29         }
    30         else {
    31             ans[l++] = ans[r--] = s1[q++];
    32             m--;
    33         }
    34     }
    35     if (m) {
    36         cout << "-1" << endl;
    37         return 0;
    38     }
    39     else {
    40         cout << ans + 1;
    41     }
    42     return 0;
    43 }

    E-智乃的数字积木(easy version)_2022牛客寒假算法基础集训营3 (nowcoder.com)

    思路:

    先按照题意同色相邻的为一段进行分段

    贪心使每段都能做大得到的结果就是最大

    对每一段进行sort还是 log2(1e5) * 1e5

    注意到 k 的范围只有10,算一下复杂度发现是10 * log2(1e5) * 1e5,原来又是个模拟题

    写个 modify 函数调用 k 次就可以了

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int p = 1e9 + 7, N = 1e6 + 10;;
     4 int n, m, k, c[N];
     5 char s[N];
     6 int modify(){
     7     for (int i = 1; i <= n;) {
     8         int j = i;
     9         while (j <= n && c[j] == c[i]) j++;
    10         sort(s + i, s + j);
    11         reverse(s + i, s + j);
    12         i = j;
    13     }
    14     int res = 0;
    15     for (int i = 1; i <= n; i++) {
    16         res = (1ll * res * 10 + s[i] - '0') % p;
    17     }
    18     return res;
    19 }
    20 int main(){
    21     ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    22     cin >> n >> m >> k >> s + 1;
    23     for (int i = 1; i <= n; i++) cin >> c[i];
    24     cout << modify() << '\n';
    25     //cout<<s+1<<'\n';
    26     while (k--) {
    27         int x, y; cin >> x >> y;
    28         for (int i = 1; i <= n; i++) if (c[i] == x) c[i] = y;
    29         cout << modify() << '\n';
    30     }
    31 }

    I-智乃的密码_2022牛客寒假算法基础集训营3 (nowcoder.com)

    思路:

    尺取还是比较好想的,只是我的实现能力有点狗屎吧

    下面复制一段大佬的代码,偶尔复习一下能学到东西

    代码:(是个压行大佬的,我这辈子都写不出这种)

     1 #include<bits/stdc++.h>
     2 #define int long long
     3 #define endl '\n'
     4 using namespace std;
     5 int judge(char c) {
     6     if (isupper(c)) return 1;
     7     if (islower(c)) return 2;
     8     if (isdigit(c)) return 3;
     9     return 4;
    10 }
    11 int a[5], ans;
    12 void answer() { cout << ans; exit(0); }
    13 int nm() { int res = 0; for (int i = 1; i <= 4; ++i) if (a[i]) res++; return res; }
    14 signed main() {
    15     int n, l = 1, L, R, r = 0, len = 0; string s;
    16     cin >> n >> L >> R >> s; s = " " + s;
    17     while (1) {
    18         while (r - l + 1 < L || nm() < 3) {
    19             r++; if (r > n) answer(); a[judge(s[r])]++;
    20         }
    21         while (r - l + 1 >= L && nm() >= 3 && l <= r) {
    22             ans += max(min(n - r + 1, l - r + R), 0ll);
    23             a[judge(s[l])]--; l++;
    24         }
    25     }
    26     return 0;
    27 }

    L-智乃的数据库_2022牛客寒假算法基础集训营3 (nowcoder.com)

    思路:

    模拟题,待补

    代码:

    待补

    I-爆炸的符卡洋洋洒洒_2022牛客寒假算法基础集训营4 (nowcoder.com)

    思路:

    稍作改动的01背包(给涛涛写)

    注意取模的操作就行

    代码:

     1 #include<bits/stdc++.h>
     2 #define int long long
     3 using namespace std;
     4 const int N = 1100;
     5 int a[N], b[N], dp[N][N];
     6 
     7 signed main() {
     8     ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
     9     int n, k; cin >> n >> k;
    10     for (int i = 1; i <= n; i++)  cin >> a[i] >> b[i];
    11     memset(dp, -0x3f, sizeof dp);
    12     dp[0][0] = 0;
    13     for (int i=1;i <= n; i++)
    14         for (int j = 0; j < k; j++)
    15         dp[i][j] = max(dp[i - 1][j], dp[i - 1][(j - a[i] % k + k) % k] + b[i]);
    16     if (dp[n][0] > 0) cout << dp[n][0];
    17     else cout << -1;
    18     return 0;
    19 }

    J-区间合数的最小公倍数_2022牛客寒假算法基础集训营4 (nowcoder.com)

    思路:

    假设把区间内每个合数都分解出来,比如说 {4, 6, 8, 9} 中,那么因子再某个数中出现次数最高有 {2: 3, 3:2}

    则上述合数的最小公倍数则为 2**3 * 3**2

    数据量只有30000,那我肯定用py啊

    代码:

     1 cnt = 0
     2 mod = int(1e9 + 7)
     3 n = 30001
     4 #质数是0,合数是1
     5 st = [0 for i in range(30005)]
     6 prime = [0 for i in range(30005)]
     7 #指数筛
     8 for i in range(2, n+1):
     9     if not st[i]:
    10         prime[cnt] = i
    11         cnt += 1
    12     for j in range(n):
    13         if prime[j] > n / i: break
    14         st[prime[j] * i] = 1
    15         if i%prime[j] == 0: break
    16 #模拟
    17 l, r = map(int, input().split())
    18 dic = {}
    19 for i in range(l, r + 1):
    20     if st[i]:
    21         tmp = i
    22         for j in prime:
    23             if tmp == 1: break
    24             tmp_cnt = 0
    25             while tmp % j == 0 and tmp != 1:
    26                 tmp /= j
    27                 tmp_cnt += 1
    28             if j not in dic or dic[j] < tmp_cnt:
    29                 dic[j] = tmp_cnt
    30 #累计答案
    31 ans = 1
    32 for i in dic:
    33     ans = ans * (i**dic[i] % mod) % mod
    34 if ans == 1:
    35     print(-1)
    36 else:
    37     print(ans)

    D-雪色光晕_2022牛客寒假算法基础集训营4 (nowcoder.com)

    思路:

    计算几何,点到射线的最短距离即是答案

    下面是py题解(人生苦短,我用py),返校去实验室机子找找lmgg的板子再用c++补(挖坑)

    题解:

     1 n=int(input())
     2 s = input()
     3 s=s.split(' ')
     4 x0=int(s[0])
     5 y_0=int(s[1])
     6 X=int(s[2])
     7 Y=int(s[3])
     8 ans=1e18
     9 for i in range(n):
    10     s=input().split(' ')
    11     x=int(s[0])
    12     y=int(s[1])
    13     A=x*x+y*y
    14     B=2*(x*(x0-X)+y*(y_0-Y))
    15     C=(x0-X)*(x0-X)+(y_0-Y)*(y_0-Y)
    16     
    17     if -B>=0and-B<=2*A:
    18         ans=min(ans,(4*A*C-B*B)/(4*A))
    19     else:
    20         ans=min(ans,min(A+B+C,C))
    21     x0+=x;
    22     y_0+=y;
    23 print(pow(ans,0.5))

    D-数位小孩_2022牛客寒假算法基础集训营5 (nowcoder.com)

    思路:

    众所周知DP是一种暴力算法,既然是暴力,那DFS说不定也可以

    相邻两数相加为质数,也就18以内的质数,数出来

    记录 last 和当前位的值,如果不是质数就break,大剪枝

    然后算一下复杂度,看似10**10,其实两个数的和为0 - 18, 一共19种,而只有7种是符合情况的质数

    那么不正确且粗略的估算下,复杂度不会过分偏离(10 * 7 / 19)** 10  ≈ 4 ** 10 = 2 ** 20 ≈ 1e6

    因此可以DFS。

    但是我写不出优美的DFS,贴上某佬的代码以供学习

    代码:

     1 #include <iostream>
     2 using namespace std;
     3 int a[20];
     4 long long l, r;
     5 int ans = 0;
     6 void dfs(long long x, int last, int flag) {
     7     if (x > r) return;
     8     if (x >= l && flag == 1) ans++;
     9     for (int i = 0; i <= 9; i++) {
    10         if (a[i + last]) dfs(x * 10 + i, i, flag || i == 1);
    11     }
    12 }
    13 
    14 int main() {
    15     cin >> l >> r;
    16     a[2] = a[3] = a[5] = a[7] = a[11] = a[13] = a[17] = 1;
    17     for (long long i = 1; i <= 9; i++) {
    18         dfs(i, i, i == 1);
    19     }
    20     cout << ans << endl;
    21 }

    2022牛客寒假算法基础集训营6_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

    思路:

    好题,数学题,一开始思路被题目描述带偏了,搁那推公式

    后面看题解发现都是暴力,算一下复杂度能过,暴力能过为什么要推公式呢?

    1、首先注意到 a 的值仅有0,1,2

    如果累乘中出现了0,那么该项为0;

    如果累乘中出现了1,那么该项不变;

    如果累乘中出现了2,那么该项 * 2;

    显然,答案应该是累加了一堆 2 的幂,先把 p2[maxn]算出来备用;

    并且,统计0,1,2的个数,存入a[3]中

    2、当某项可以产生贡献时,该项是个n次多项式,其中k项为1,则有n-k项为2

    从a[1]中选择k项,从a[2]中选择n - k项,组合数经典问题,因此提前求出逆元inv[maxn]备用

    代码:

     1 #include<bits/stdc++.h>
     2 #define int long long
     3 using namespace std;
     4 const int N = 1e7 + 10, mod = 998244353;
     5 
     6 int ksm(int a, int b){
     7     int ans = 1;
     8     while (b)    {
     9         if (b & 1)ans = ans * a % mod;
    10         b >>= 1;
    11         a = a * a % mod;
    12     }
    13     return ans;
    14 }
    15 
    16 int f[N], inv[N], p2[N], a[5];
    17 
    18 signed main(){
    19     int n, k; cin >> n >> k;
    20     for (int i = 1; i <= n; i++){
    21         int x; cin >> x;
    22         a[x]++;
    23     }
    24     f[0] = inv[0] = p2[0] = 1;
    25     for (int i = 1; i <= max(a[1], a[2]); i++){
    26         p2[i] = p2[i - 1] * 2 % mod;
    27         f[i] = f[i - 1] * i % mod;
    28         inv[i] = inv[i - 1] * ksm(i, mod - 2) % mod;
    29     }
    30     int ans = 0;
    31     for (int i = 0; i <= min({ k,a[2] }); i++){
    32         int y = i, x = a[2];
    33         int z = ((f[x] * inv[y] % mod) * inv[x - y] % mod) * p2[i] % mod;
    34         x = a[1], y = k - i;
    35         ans = ans + ((z * (f[x] * inv[y] % mod) % mod) * inv[x - y] % mod) % mod;
    36         ans %= mod;
    37     }
    38     cout << ans << endl;
    39 }

    B-价值序列_2022牛客寒假算法基础集训营6 (nowcoder.com)

    思路:

    单调子串可以删掉中间的数(只保留波峰和波谷)不改变贡献

    其余数可删可不删,也就是01的情况

    找出所有可以删掉的数的数量 k,然后求 pow(2, k)即可

    代码:

     1 #include <bits/stdc++.h>
     2 #define int long long
     3 using namespace std;
     4 const int maxn = 2e5 + 5;
     5 const int mod = 998244353;
     6 int arr[maxn], p2[maxn];
     7 
     8 signed main() {
     9     ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
    10     p2[0] = 1;
    11     for (int i = 1; i <= 100000; i++) p2[i] = p2[i - 1] * 2 % mod;
    12     int t; cin >> t; while (t--) {
    13         int n, res = 1; cin >> n;
    14         for (int i = 1; i <= n; i++) cin >> arr[i];
    15         for (int i = 1; i <= n; i++) {
    16             int j = i;
    17             while (j < n && arr[j + 1] == arr[i]) j++; //值不改变
    18             if (i - 1 >= 1 && j + 1 <= n && //范围内
    19                 (arr[i - 1] < arr[i] && arr[j + 1] > arr[i] || //单调上升 
    20                 arr[i - 1] > arr[i] && arr[j + 1] < arr[i])) { //单调下降
    21                 res = res * p2[2, j - i + 1] % mod;
    22             }
    23             else {
    24                 res = res * (p2[2, j - i + 1] + mod - 1) % mod;
    25             }
    26             i = j;
    27         }
    28         cout << res << endl;
    29     }
    30     return 0;
    31 }

    D-删除子序列_2022牛客寒假算法基础集训营6 (nowcoder.com)

    思路:

    从左往右枚举当前下标的状态

    贪心的想,删字符肯定是尽量靠左,给右边删除其他的留空间

    动态规划的想,如果当前位置有机会为答案产生贡献,其左边一定可以组成前缀串

    用一个一维数组记录被贡献串某下标可以组成的前缀串数量

    由于没有重复字母,降低了难度,只需要26 * 1e6就能过

    代码:

     1 #include <bits/stdc++.h>
     2 //#define int long long
     3 using namespace std;
     4 const int maxn = 2e5 + 5;
     5 int p[30];
     6 
     7 signed main() {
     8     ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
     9     int t; cin >> t; while (t--) {
    10         memset(p, 0, sizeof p);
    11         int n, m; cin >> n >> m;
    12         string s1, s2; cin >> s1 >> s2;
    13         for (int i = 0; s1[i]; i++) {
    14             for (int j = 0; s2[j]; j++) {
    15                 if (s1[i] == s2[j]) { // 有机会产生贡献
    16                     if (j == 0) { // 作为开头或是延长左边字串的贡献
    17                         p[j]++;
    18                     }
    19                     else if (p[j - 1]) { // 道路不断向前延伸(我在干什么啊
    20                         p[j]++, p[j - 1]--;
    21                     }
    22                 }
    23             }
    24         }
    25         cout << p[m - 1] << '\n';
    26     }
    27     return 0;
    28 }

    Problem - D - Codeforces

    题意:

    在给定的数组中,选择某相邻两数间的位置进行操作

    操作:任意选择一个数,在任意位置插入两次,使串最终变成多组对称串

    输出:

    第一行:操作次数 x;

    接下来 x 行:操作位置 p 和插入数 n

    思路:

    我们想象一个隔板,左边是当前拥有的,右边是还未进行操作的

    用桶把左边的数装起来,对于某个数字 x,如果他的数量是1,那么无论如何进行操作,由于每次增加偶数个,因此不可能消掉 x

    现在隔板向右移动一格,将一个数字加入了左边

    如果加入数字后,这个桶里存在两个相同的数,那么肯定可以消除:凑回文串

    那么我们可以通过一直进行凑回文的操作,保证左边的桶里都只有0或1个数(出现2时消除变成0)来构造

    赛时想到了,但是我的代码水平,那就是一坨*

    代码:

    抄某个大佬的,有空照着模一模

     1 #include<bits/stdc++.h>
     2 #define int long long
     3 #define PII pair<int,int>
     4 using namespace std;
     5 
     6 const int N = 505;
     7 int a[N],tmp[N],n;
     8 map<int,int> mp;
     9 vector<int> v;
    10 vector<PII> ans;
    11 void solve(){
    12     cin>>n;
    13     for(int i=1;i<=n;i++)
    14         cin>>a[i];
    15     mp.clear();
    16     for(int i=1;i<=n;i++) 
    17         mp[a[i]]++;
    18     bool flag = 1;
    19     for(PII p:mp){
    20         if(p.second%2==1){
    21             flag=0;
    22             break;
    23         }
    24     }
    25     if(!flag){
    26         cout<<"-1\n";
    27         return ;
    28     }
    29     
    30     v.clear();
    31     ans.clear();
    32     int now=1,last=0;
    33     while(now<=n){
    34         int index=-1;
    35         for(int i=now+1;i<=n;i++){
    36             if(a[i]==a[now]){
    37                 index=i;
    38                 break;
    39             }
    40         }
    41         v.push_back((index-now)*2);//该子段配置完毕后的长度贡献
    42         for(int i=now+1;i<index;i++)
    43             ans.push_back({last+index-now+i-now,a[i]});
    44         
    45         for(int i=now+1;i<index;i++)
    46             tmp[i]=a[index-(i-now)];
    47         for(int i=now+2;i<=index;i++)
    48             a[i]=tmp[i-1];
    49         //以上操作为进行数组内的子段倒置
    50             
    51         last+=(index-now)*2;//最后一组配对完后的下标 
    52         now+=2;//下一个待构造
    53     }
    54     cout<<ans.size()<<endl;
    55     for(auto p:ans)
    56         cout<<p.first<<" "<<p.second<<endl;
    57     cout<<v.size()<<endl;
    58     for(auto val:v)
    59         cout<<val<<" ";
    60     cout<<endl;
    61 }
    62 
    63 signed main(){
    64     int t;
    65     cin>>t;
    66     while(t--){
    67         solve();
    68     }
    69     return 0;
    70 }
  • 相关阅读:
    Android -- DiskLruCache
    Android -- EventBus解析
    Android -- Annotation
    Ubuntu 1604 安装配置 kafka,并配置开机自启(systemctl)
    zookeeper/kafka的部署
    pdf 中内容的坐标系
    C# 获取Windows 设备信息
    C#读取Word指定页的内容
    再看C# ThreadPool与Task的认识总结
    同步IO、异步IO、阻塞IO、非阻塞IO之间的联系与区别
  • 原文地址:https://www.cnblogs.com/Yuuuukino/p/15991682.html
Copyright © 2020-2023  润新知