• 【ATcoder】AtCoder Beginner Contest 159 题解


    官方题解

    落谷链接

    ATC链接

    A - The Number of Even Pairs

    题意

    给你两个数$n, m$代表有$n$个偶数,$m$个奇数。让你输出$n$个偶数$m$个奇数从中任选两个数(没有顺序)相加结果为偶数的个数。

    题解

    相加为偶数只有偶加偶和奇加奇两种情况,其实就是在$n$个数中取两个($Cinom{2}{n}$),在$m$个数中取两个($Cinom{2}{m}$)。

    时间复杂度$O(1)$

    1 #include <iostream>
    2 using namespace std;
    3 int main() {
    4     long long n, m;
    5     cin >> n >> m;
    6     cout << n * (n - 1) / 2 + m * (m - 1) / 2;
    7     return 0;
    8 }
    A - The Number of Even Pairs

    B - String Palindrome

    题意

    定义一个字符串 $S$ 是强回文串当且仅当 $S$,$S_{1...frac{(|s|+1)}{2}}$ 和 $S_{|s|-frac{(|s|+3)}{2}+1...|s|}$都是回文的。判断一个回文串是不是强回文串。$3 leq |s| leq 99$且$|s|$是奇数。

    题解

    直接取出每一部分的字符串,判断到中心距离相等的位置的字符是否相等即可。

    时间复杂度$O(|s|)$。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 using namespace std;
     5 char s[100], tmp[100];
     6 int top;
     7 int n, nn;
     8 int main() {
     9     scanf("%s", s + 1);
    10     n = strlen(s + 1);
    11     nn = n;
    12     for (int i = 1; i <= nn; i++) {
    13         if (s[i] != s[nn - i + 1]) {
    14             puts("No");
    15             return 0;
    16         }
    17     }
    18     nn = (n - 1) / 2;
    19     for (int i = 1; i <= nn; i++) {
    20         if (s[i] != s[nn - i + 1]) {
    21             puts("No");
    22             return 0;
    23         }
    24     }
    25     nn = (n + 3) / 2;
    26     for (int i = nn; i <= n; i++) {
    27         tmp[++top] = s[i];
    28     }
    29     for (int i = 1; i <= top; i++) {
    30         if (tmp[i] != tmp[top - i + 1]) {
    31             puts("No");
    32             return 0;
    33         }
    34     }
    35     puts("Yes");
    36     return 0;
    37 }
    B - String Palindrome

    C - Maximum Volume

    题意

    给定一个整数x。求所有各棱长为实数且和为x的长方体中最大的体积是多少。

    题解

    小学奥数题...

    和一定差小积大。

    其实就是一个均值不等式

    设棱长为$a,b,c$,则 $a imes b imes c leq frac{(a+b+c)^3}{27}=frac{x^3}{27}$,当$a=b=c$时成立。

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 double l;
     5 int main() {
     6     scanf("%lf", &l);
     7     l = l / 3;
     8     printf("%.12lf", l * l * l);
     9     return 0;
    10 }
    C - Maximum Volume

     D - Banned K

    题意

    我们有n个数,每个数都在[1,n]中,如果去掉第k个数,问剩下的数有多少对相同的数(不计顺序),$1 leq n leq 2 imes 10^5$。

    题解

    我们可以把 $n$ 个数中有多少相同的数对,因为每个数都很小所以这点我们可以用堆来做。

    设 $cnt[i]$ 代表 $i$ 这个数出现的次数所以答案等于 $sum_{i=1}^{n}frac{cnt[i] imes (cnt[i]-1)}{2}$。

    现在我们来看删掉一个值为 $x$ 的数。

    $cnt[x]$ 的值减小了一,它的贡献变成了 $frac{(cnt[x]-1) imes (cnt[x]-2)}{2}$,相比之前减少了 $cnt[x]-1$。

    设不删时的答案是 $tot$,则删掉一个权值为 $x$ 的数的答案为 $tot-(cnt[x]-1)$。

    单词询问复杂度O(1)。

     1 //这里sum就是cnt
     2 #include <iostream>
     3 #include <cstdio>
     4 using namespace std;
     5 const int N = 2e5 + 10;
     6 int n, a[N];
     7 long long sum[N], tot;
     8 int main() {
     9     scanf("%d", &n);
    10     for (int i = 1; i <= n; i++) scanf("%d", &a[i]), sum[a[i]]++;
    11     for (int i = 1; i <= n; i++) {
    12         tot += sum[i] * (sum[i] - 1) / 2;
    13     }
    14     for (int i = 1; i <= n; i++) {
    15         printf("%lld
    ", tot - (sum[a[i]] - 1));
    16     }
    17     return 0;
    18 }
    D - Banned K

    E - Dividing Chocolate

    我太弱了,我比赛时这题竟然都没调出来。

    题意

    有一个$n imes m$的矩阵,每个位置要不是要不不是零,用尽量少的次数把这个矩阵切成几块(只能把整行或整列与下一行或列切开,具体可看下面的例子)使得每一块中1的个数少于某个常数。

    $1 leq n leq 10, 1 leq m leq 1000$

    题解

    我们发现n非常的小,于是我们可以枚举行之间且的情况,再判断列之间要切的情况,取最小值即可。

    时间复杂度$O(2^n imes n imes m)$

    下面代码是E - Dividing Chocolate

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring> 
     4 using namespace std;
     5 int n, m, k;
     6 int cnt[15][1005];
     7 char mp[15][1005];
     8 bool vis[1005];
     9 int cut[20], top;
    10 int ans = 0x7f7f7f7f;
    11 int cal(int x1, int x2, int y1, int y2) {
    12     return cnt[x2][y2] - cnt[x2][y1 - 1] - cnt[x1 - 1][y2] + cnt[x1 - 1][y1 - 1];
    13 }
    14 void dfs(int pos) {
    15     cut[++top] = pos;
    16     if (pos == n) {
    17         memset(vis, 0, sizeof(vis)); 
    18         int sx, sy, lst = 0, tmp = 0;
    19         for (int j = 1; j <= m; j++) {
    20             for (int i = 1; i <= top; i++) {
    21                 sx = cut[i - 1] + 1, sy = lst + 1;
    22                 if (cal(sx, cut[i], sy, j) > k) {
    23                     if (lst == j - 1) {
    24                         top--;
    25                         return;
    26                     } else {
    27                         sy = j;
    28                         if (!vis[j - 1]) tmp++, vis[j - 1] = 1;
    29                     }
    30                 }
    31             }
    32             if (vis[j - 1]) lst = j - 1;
    33         }
    34         ans = min(ans, tmp + top - 1);
    35         top--;
    36         return;
    37     }
    38     for (int i = pos + 1; i <= n; i++) {
    39         dfs(i);
    40     }
    41     top--;
    42 }
    43 int main() {
    44     scanf("%d%d%d", &n, &m, &k);
    45     for (int i = 1; i <= n; i++) {
    46         scanf("%s", mp[i] + 1); 
    47         for (int j = 1; j <= m; j++) {
    48             cnt[i][j] = cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1] + mp[i][j] - '0';
    49         }
    50     }
    51     for (int i = 1; i <= n; i++) {
    52         dfs(i);
    53     }
    54     cout << ans;
    55     return 0;
    56 }
    F - Knapsack for All Segments

    F - Knapsack for All Segments

    题意

    一个长度为n的数组,求任意[ L,R ]区间子序列和为S的总数的和。

    题解

    看到和为s,很容易想到背包,其实这就是一道01背包的变形。

    考虑一组和为s的排列x1<x2<...<xk,所有[1,x1]内的L,[xk, n]之内的R都包含这组排列,所以它的贡献就是$x_1 imes (n-x_k+1)$。

    设dp[i][j]表示枚举到第i个数,和为j的排列的第一个数的和。

    显然dp[0][j]都是0。转移方程类似01背包,dp[i][j]=dp[i-1][j]+(j==a[i]?i:0)。

    考虑如何统计答案,如果a[i]=s,ans+=i*(n-i+1);如果a[i]>s,ans+=dp[s-a[i]]*(n-i+1)。如果a[i]<s则不用更新。

    当然类似于背包问题,这题也可以用滚动背包优化空间复杂度。

    时间复杂度O(ns);空间复杂度O(n+s)

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 const long long N = 3010;
     5 const long long S = 3010;
     6 const long long mod = 998244353;
     7 long long n, s;
     8 long long a[N];
     9 long long dp[S];
    10 long long ans;
    11 int main() {
    12     scanf("%lld%lld", &n, &s);
    13     for (long long i = 1; i <= n; i++) scanf("%lld", &a[i]);
    14     for (long long i = 1; i <= n; i++) {
    15         if (s == a[i]) {
    16             ans += i * (n - i + 1) % mod;
    17             ans %= mod;
    18         } else if (s > a[i]) {
    19             ans += dp[s - a[i]] * (n - i + 1) % mod;
    20             ans %= mod;
    21         }
    22         for (long long j = s; j >= a[i]; j--) {
    23             dp[j] += (dp[j - a[i]] + (j == a[i] ? i : 0)) % mod;
    24             dp[j] %= mod;
    25         }
    26     } 
    27     printf("%lld", ans);
    28     return 0;
    29 }
    F - Knapsack for All Segments
  • 相关阅读:
    逆向工程工具介绍2-IDA
    汇编语言基础-1 基本语言元素
    Python常用标准库1-Turtle,Random,Time和Datetime
    Python的模块、包和库的概念
    Go语言的函数修饰符
    物理层2-物理层下面的传输媒体
    数据分析之两种用户分群方法(RFM和聚类)
    区间估计与假设检验公式
    源码分析过滤器与拦截器的区别
    Springboot拦截器使用及其底层源码剖析
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/12550421.html
Copyright © 2020-2023  润新知