zoukankan      html  css  js  c++  java
  • [Nowcoder]2020牛客寒假算法基础集训营2

    20200206 第二场

    进度(10 / 10)

    A、做游戏

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/A

    2、题面

    牛牛和 牛可乐在玩石头剪刀布。

    众所周知,石头剪刀布的规则是这样的:

    - 在一局游戏中,双方各自出石头、剪刀、布其一。
    - 胜负关系如下:


    牛牛和 牛可乐进行了多轮游戏, 牛牛总共出了 A 次石头,B 次剪刀,C 次布;牛可乐总共出了 X 次石头,Y 次剪刀,Z 次布。 你需要求出牛牛最多获胜多少局。

    3、思路

    小型贪心。牛牛出石头时牛可乐优先出剪刀,同理剪刀-布,布-石头。三次比较均取两者较小值,剩下的便是不可能获胜的局了。

    4、代码

     1 #include <bits/stdc++.h> 
     2 using namespace std;
     3 
     4 long long a, b, c, d, e, f;
     5 
     6 int main() {
     7     cin >> a >> b >> c >> d >> e >> f;
     8     cout << min(a, e) + min(b, f) + min(c, d);
     9     return 0;
    10 }

    B、排数字

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/B

    2、题面

    牛可乐 最喜爱的字符串是 616。

    牛可乐得到了一个纯数字的字符串 S,他想知道在可以任意打乱 S 顺序的情况下,最多有多少个不同的子串为 616。

    当两个子串 S[l1…r1],S[l2…r2] 满足 l1≠l2 或 r1≠r2 时它们被认为是不同的。

    3、思路

    签到题。将字符串中1和6提取出来,以616161...的形式排列为最优解,则统计1和6的个数并处理一下即可得到答案。

    4、代码

     1 #include <bits/stdc++.h> 
     2 using namespace std;
     3 
     4 #define MAXN 200005
     5 
     6 int n, a, b;
     7 char s[MAXN];
     8 
     9 int main() {
    10     cin >> n >> s;
    11     for (int i = 0; i < n; i++) a += s[i] == '1', b += s[i] == '6';
    12     cout << min(a, b - 1);
    13     return 0;
    14 }

    C、算概率

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/C

    2、题面

    牛牛刚刚考完了期末,尽管 牛牛 做答了所有 n 道题目,但他不知道有多少题是正确的。
    不过,牛牛 知道第 i 道题的正确率是 pi​。
    牛牛 想知道这 n 题里恰好有 0,1,…,n 题正确的概率分别是多少,对 10^9+7 取模。
    对 10^9+7 取模的含义是:对于一个 b≠0 的不可约分数 a/b​,存在 q 使得 b * q mod (10^9+7) =a,q 即为 a / b​ 对 10^9+7 取模的结果。

    3、思路

    自己瞎捣鼓了许久没做出来。看了下题解,发现我duck不必纠结于将pi化成分数形式。

    这也算是一道DP题。设f[i][j]表示前i道题里j道题正确的概率,状态转移:f[i][j] = f[i - 1][j - 1] * p[i] + f[i - 1][j] * p_[i],其中p_[i]表示做错的概率。

    根据题意,p_[i] = mod + 1 - p[i],上述数据全部需要取模。

    4、代码

     1 #include <bits/stdc++.h> 
     2 using namespace std;
     3 
     4 typedef long long ll;
     5 
     6 #define MAXN 2005
     7 #define MOD 1000000007
     8 
     9 ll n, a[MAXN], b[MAXN], f[MAXN][MAXN];
    10 
    11 int main() {
    12     cin >> n;
    13     for (int i = 1; i <= n; i++)
    14         cin >> a[i], b[i] = (MOD + 1 - a[i]) % MOD; 
    15     f[0][0] = 1; 
    16     for (int i = 1; i <= n; i++) {
    17         f[i][0] = (f[i - 1][0] * b[i]) % MOD;
    18         for (int j = 1; j <= i; j++)
    19             f[i][j] = ((f[i - 1][j] * b[i]) % MOD + (f[i - 1][j - 1] * a[i]) % MOD) % MOD;
    20     }
    21     for (int i = 0; i <= n; i++) cout << f[n][i] << ' ';
    22     return 0;
    23 }

    D、数三角

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/D

    2、题面

    牛牛得到了一个平面,这个平面上有 n 个不重合的点,第 i 个点的坐标为 (xi,yi)。

    牛牛想知道,这 n 个点形成的三角形中,总共有多少个钝角三角形。

    3、思路

    平面几何题。枚举任意两个点,再选择第三个点,看是否满足钝角三角形的条件。设当前选择的两个点为A, B,如图所示,

    根据几何知识,当且仅当第三个点位于如下两种位置,可以与A, B组成钝角三角形:

    ①以AB中点为圆心的圆内;

    ②不在与AB所连的线段垂直且分别过A, B的两条直线之间(排除①的圆内)。

    文字描述显得比较拗口,但图示就比较清晰了。那么:

    对于①,求得中点和半径,判定第三个点和圆心距离是否小于半径即可;

    对于②,求得直线AB的斜率,进而求得与之垂直的直线的斜率k,分别将A, B, 所求的第三个点代入求得所在直线的截距b,如果第三个点的b小于A的或者大于B的,即满足条件。

    平面几何题一个常见的要考虑的问题就是斜率为0或者斜率为无穷大的情况,需要特殊考虑。

    4、代码

     1 #include <bits/stdc++.h> 
     2 using namespace std;
     3 
     4 #define MAXN 200005
     5 
     6 int n, ans;
     7 double x[MAXN], y[MAXN];
     8 
     9 double dis(int a, int b) {
    10     return sqrt((y[a] - y[b]) * (y[a] - y[b]) + (x[a] - x[b]) * (x[a] - x[b]));
    11 }
    12 
    13 int main() {
    14     cin >> n;
    15     for (int i = 1; i <= n; i++) cin >> x[i] >> y[i];
    16     for (int i = 1; i <= n - 2; i++)
    17         for (int j = i + 1; j <= n - 1; j++) {                
    18                 x[0] = (x[j] + x[i]) / 2.0, y[0] = (y[j] + y[i]) / 2.0;
    19                 double r = dis(i, 0);
    20                 for (int l = j + 1; l <= n; l++) {
    21                     if (y[j] == y[i])
    22                         ans += (x[l] < min(x[i], x[j]) || x[l] > max(x[i], x[j])) && y[l] != y[i];
    23                     else if (x[j] == x[i])
    24                         ans += (y[l] < min(y[i], y[j]) || y[l] > max(y[i], y[j])) && x[l] != x[i];
    25                     else {
    26                         double k = -1 / ((y[j] - y[i]) / (x[j] - x[i]));
    27                         double bi = y[i] - k * x[i], bj = y[j] - k * x[j]; 
    28                         if (-1 / ((y[l] - y[i]) / (x[l] - x[i])) == k) continue; 
    29                         double bl = y[l] - k * x[l];
    30                         ans += bl < min(bi, bj) || max(bi, bj) < bl;
    31                     }
    32                     ans += dis(l, 0) < r;
    33                 }
    34         }
    35     cout << ans;
    36     return 0;
    37 }

    E、做计数

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/E

    2、题面

    这一天,牛牛与 牛魔王相遇了――然而这并不在 牛牛期望之中。
    牛魔王不出意料又给 牛牛一道看似很难的题目:求有多少个不同的正整数三元组 (i,j,k) 满足 且 i×j≤n。

    牛牛并不会做,你能略施援手吗?
    当两个三元组 (i1,j1,k1),(i2,j2,k2) 满足 i1≠i2 或 j1≠j2 或 k1≠k2 时它们被认为是不同的。

    3、思路

    看到根号第一反应是平方。数学符号太难打了,直接复制别人的题解好了。

    然后我就一个个i去枚举,,果不其然就T了。想了许久也没找到个好办法,看了题解给的code:

    枚举小于n的完全平方数x,再枚举x的因子,仅需在sqrt(x)范围内枚举,由于(i, j)和(j, i)不等价,再在结果上*2即可,当然(i, i)需要特殊考虑。

    4、代码

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 int n, ans;
     5 
     6 int main() {
     7     cin >> n;
     8     for (int i = 1; i <= sqrt(n); i++) {
     9         int o = i * i;
    10         for (int j = 1; j <= sqrt(o); j++)
    11             ans += i == j ? 1 : o % j == 0 ? 2 : 0;
    12     }
    13     cout << ans;
    14     return 0;
    15 }

    F、拿物品

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/F

    2、题面

    牛牛和 牛可乐 面前有 n 个物品,这些物品编号为 1,2,…,n ,每个物品有两个属性 ai,bi 。
    牛牛与 牛可乐会轮流从剩下物品中任意拿走一个, 牛牛先选取。

    设 牛牛选取的物品编号集合为 H,牛可乐选取的物品编号的集合为 T,取完之后,牛牛 得分为 ∑i∈Hai;而 牛可乐得分为 ∑i∈Tbi。

    牛牛和 牛可乐都希望自己的得分尽量比对方大(即最大化自己与对方得分的差)。

    你需要求出两人都使用最优策略的情况下,最终分别会选择哪些物品,若有多种答案或输出顺序,输出任意一种。

    3、思路

    贪心。这题思路不错,值得细品的一种贪心。

    由于目的是使得分差最大,对于物品i,A得ai,B得bi,如果A拿了,相当于A得了分,而B同时没得到分,则相当于分差拉开了ai +bi。

    根据这个贪心思路,我们将物品按照ai + bi从大到小的顺序排序,然后两人依次拿即可。

    4、代码

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define MAXN 200005
     5 
     6 int n, ans[2][MAXN], o = 1;
     7 
     8 struct item {
     9     int p[3], o;
    10 } a[MAXN];
    11 
    12 struct cmp {
    13     bool operator () (item a, item b) {
    14         return a.p[2] > b.p[2];
    15     }
    16 } x;
    17 
    18 int main() {
    19     cin >> n;
    20     for (int j = 0; j <= 1; j++) 
    21         for (int i = 1; i <= n; i++) cin >> a[i].p[j], a[i].o = i;
    22     for (int i = 1; i <= n; i++) a[i].p[2] = a[i].p[0] + a[i].p[1];
    23     sort(a + 1, a + n + 1, x);
    24     for (int i = 1; i <= n; i++) o ^= 1, ans[o][(i - 1) / 2 + 1] = a[i].o;
    25     for (int i = 1; i <= n - n / 2; i++) cout << ans[0][i] << ' ';
    26     cout << endl;
    27     for (int i = 1; i <= n / 2; i++) cout << ans[1][i] << ' ';
    28     return 0;
    29 }

    G、判正误

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/G

    2、题面

    牛可乐有七个整数 a,b,c,d,e,f,g 并且他猜想 a^d+b^e+c^f=g。但牛可乐无法进行如此庞大的计算。
    请验证牛可乐的猜想是否成立。

    3、思路

    这么憨批的签到题我还去推式子。。想着应该没这么简单。

    然后写个快速幂又被卡常@!%@#¥#

    可以直接用pow,可以写快速幂,快速幂模数设成1e9+7可过,列一下常用模数方便以后用:

    12255871, 16341163, 21788233, 29050993, 38734667, 51646229, 68861641,  91815541, 1000000007, 1000000009

    4、代码

     1 #include <bits/stdc++.h> 
     2 using namespace std;
     3 
     4 typedef long long ll;
     5 #define MOD 1000000007
     6 
     7 ll t, a, b, c, d, e, f, g;
     8 
     9 ll qpow(ll x, ll y) {
    10     ll o = x, res = 1;
    11     while (y) {
    12         if (y & 1) (res *= o) %= MOD;
    13         (o *= o) %= MOD, y >>= 1;
    14     }
    15     return res;
    16 }
    17 
    18 int main() {
    19     cin >> t;
    20     for (int i = 1; i <= t; i++) {
    21         cin >> a >> b >> c >> d >> e >> f >> g;
    22         printf(qpow(a, d) + qpow(b, e) + qpow(c, f) == g ? "Yes
    " : "No
    ");
    23     }
    24     return 0;
    25 }

    H、施魔法

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/H

    2、题面

    牛可乐有 n 个元素( 编号 1..n ),第 i 个元素的能量值为 ai。
    牛可乐可以选择至少 k 个元素来施放一次魔法,魔法消耗的魔力是这些元素能量值的极差。形式化地,若所用元素编号集合为 S,则消耗的魔力为 max⁡i∈S{ai}−min⁡i∈S{ai}。

    牛可乐要求每个元素必须被使用恰好一次。
    牛可乐想知道他最少需要多少魔力才能用完所有元素,请你告诉他。

    3、思路

    大概知道是个DP,但思路有问题,总是困扰于O(n ^ 2)的算法。

    首先元素顺序无关,可以将其从小到大排序。f[i]表示前 i 个元素全部使用一次,最少需要的魔力。

    考虑到对于第 i 个元素,可以与第i - 1个元素所在的那次魔法组合,即f[i] = f[i - 1] + (a[i] - a[i - 1]);也可以作为一个魔法的第 k 大元素,即f[i] = f[i - k] + (a[i] - a[i - k + 1]),两者取最小值即可。

    另外,前 k 个元素必然组成一个魔法,所以先预处理出来:f[k] = a[k] - a[1]。

    4、代码

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define MAXN 300005
     5 #define INF 0x3f3f3f3f
     6 
     7 int f[MAXN], a[MAXN], n, k;
     8 
     9 int main() {
    10     cin >> n >> k;
    11     memset(f, INF, sizeof(f));
    12     for (int i = 1; i <= n; i++) cin >> a[i];
    13     sort(a + 1, a + n + 1);
    14     f[k] = a[k] - a[1];
    15     for (int i = k + 1; i <= n; i++)
    16         f[i] = min(f[i - 1] - a[i - 1] + a[i], f[i - k] - a[i - k + 1] + a[i]);
    17     cout << f[n]; 
    18 }

    I、施魔法

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/I

    2、题面

    在无垠的宇宙中,有 n 个星球,第 i 个星球有权值 vi​。
    由于星球之间距离极远,因此想在有限的时间内在星际间旅行,就必须要在星球间建立传送通道。
    任意两个星球之间均可以建立传送通道,不过花费并不一样。第 i 个星球与第 j 个星球的之间建立传送通道的花费是 lowbit(vi⊕vj),其中 ⊕ 为二进制异或,而 lowbit(x) 为 x 二进制最低位 1 对应的值,例如 lowbit(5)=1,lowbit(8)=8。特殊地,lowbit(0)=0。
    牛牛想在这 n 个星球间穿梭,于是――你需要告诉 牛牛,要使这 n 个星球相互可达,需要的花费最少是多少。

    3、思路

    题解思路非常巧妙,我很喜欢,就是解释的太草率了,看半天没看懂。

    题目把花费定义为二进制相关,看起来很麻烦,其实提炼出来很简单——从二进制最低位找起,如果存在第 k 位,使得既存在该位为0的权值又存在该位为1的权值,如图所示:

    由于只需让n个点连通即可,即连接n - 1条线。第 k 位前的二进制位对于所有权值全部相同,不再需要考虑;第 k 位分为 0 和 1 两种情况,如图所示,我们将它们分成两部分,并分别选出一个点作为代表,对于每一个该位为 0 的点和代表该位为 1 的点相连,对于每一个该位为 1 的点和代表该位为 0 的点相连,总共连接了 n 次,由于两个代表之间连接了两次,删去一次,即n - 1次。而每一次连接,代价均为lowbit() = 2 ^ k,故总代价为 (n - 1) * 2 ^ k。(右图移动了点的位置,以方便看清结构)

    不过做到这里还有一个细节没有考虑——如果存在两个点权值相同,即相连代价为0,则可以得到更优解。如图所示,如果存在这样2对相同权值的点,则可以少连2条代价为2 ^ k的线,所以我们可以先对所有权值排序并统计出相同权值的节点对数个数m,最后的总代价为 (n - m - 1) * 2 ^ k。

    最后对代码进行一些解释:

    为得到上述的第 k 位,我们定义两个变量:x表示对权值连续取和,y表示对权值连续取或。根据二进制相关知识,当且仅当某一位存在1,连续取和结果为1;当且仅当某一位存在0,连续取或结果为0;再将 x 异或 y,所得结果如果某一位为1,表示x该位为1,y为0,再从最低位枚举,可得第 k 位。

    4、代码

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define MAXN 200005
     5 #define INF 0x7fffffff
     6 
     7 int n, x = INF, y, v[MAXN], m, ans;
     8 
     9 int main() {
    10     cin >> n;
    11     for (int i = 1; i <= n; i++) {
    12         cin >> v[i];
    13         x &= v[i], y |= v[i];
    14     }
    15     x ^= y;
    16     sort(v + 1, v + n + 1);
    17     for (int i = 1; i <= n - 1; i++) 
    18         m += v[i] == v[i + 1];
    19     for (int i = 0, o = 1; i <= 30; i++, o <<= 1)
    20         if (x & o) {
    21             ans = (n - m - 1) * o;
    22             break;
    23         }
    24     cout << ans;
    25     return 0;
    26 }

    (有个迷惑的地方是,我开始是直接输出的,即 cout << (n - m - 1) * o;  死活有一个点过不了,是什么原因?)

     破案了,因为可能出现没结果的情况,然后默认输出0就能过(这是题目的锅⑧

    J、求函数

    1、链接

    https://ac.nowcoder.com/acm/contest/3003/J

    2、题面

    牛可乐有 n 个一次函数,第 i 个函数为 fi(x)=ki*x+bi。

    牛可乐有 m 次操作,每次操作为以下二者其一:

    • 1 i k b 将 fi(x)修改为 fi(x)=k*x+b。
    • 2 l r 求 fr(fr−1(⋯(fl+1(fl(1)))⋯ ))。

    牛可乐当然(bu)会做啦,他想考考你——
    答案对 10^9+7 取模。

    3、思路

    一道不错的线段树题。

    代码中Tree结构体

    4、代码

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 #define MAXN 200005
     5 #define MOD 1000000007
     6 
     7 #define ls o << 1
     8 #define rs o << 1 | 1
     9 
    10 typedef long long ll;
    11 
    12 ll n, m, k[MAXN], b[MAXN];
    13 ll ox, ok, ob, ql, qr, p;
    14 
    15 struct Tree {
    16     ll x, y;
    17     Tree() {}
    18     Tree(ll _x, ll _y) { x = _x, y = _y; }
    19 } t[MAXN << 2];
    20 
    21 void build(int o, int l, int r) {
    22     if (l == r) {
    23         t[o] = Tree(k[l], b[l]);
    24         return;
    25     }
    26     int m = (l + r) >> 1;
    27     build(ls, l, m), build(rs, m + 1, r);
    28     t[o] = Tree(t[ls].x * t[rs].x % MOD, (t[ls].y * t[rs].x + t[rs].y) % MOD);
    29 }
    30 
    31 void upd(int o, int l, int r) {
    32     if (l == r) {
    33         t[o] = Tree(ok, ob);
    34         return;
    35     }
    36     int m = (l + r) >> 1;
    37     if (ox <= m) upd(ls, l, m);
    38     else upd(rs, m + 1, r);
    39     t[o] = Tree(t[ls].x * t[rs].x % MOD, (t[ls].y * t[rs].x + t[rs].y) % MOD);
    40 }
    41 
    42 Tree query(int o, int l, int r) {
    43     Tree rl, rr;
    44     if (ql <= l && r <= qr) return Tree(t[o].x, t[o].y);
    45     int m = (l + r) >> 1, fl = 0, fr = 0;
    46     if (ql <= m) rl = query(ls, l, m), fl = 1;
    47     if (qr > m) rr = query(rs, m + 1, r), fr = 1;
    48     return fl && fr ? Tree(rl.x * rr.x % MOD, (rl.y * rr.x + rr.y) % MOD) : fl ? rl : rr; 
    49 }
    50 
    51 int main() {
    52     cin >> n >> m;
    53     for (int i = 1; i <= n; i++) cin >> k[i];
    54     for (int i = 1; i <= n; i++) cin >> b[i];
    55     build(1, 1, n);
    56     for (int i = 1; i <= m; i++) {
    57         cin >> p;
    58         if (p == 1) {
    59             cin >> ox >> ok >> ob;
    60             upd(1, 1, n);
    61         }
    62         else {
    63             cin >> ql >> qr;
    64             Tree ans = query(1, 1, n);
    65             cout << (ans.x + ans.y) % MOD << endl;
    66         }
    67     }
    68     return 0;
    69 } 

    疑惑:第一次交了然后TLE了,调了半天,还去和标程对比了老半天,然后再交一发一模一样的告诉我A了?黑人问号.jpg

  • 相关阅读:
    系统架构设计(通用型)
    主流Java数据库连接池分析(C3P0,DBCP,TomcatPool,BoneCP,Druid)
    JS实现多行文本最后是省略号紧随其后还有个超链接在同一行的需求
    java判断集合是否相等
    JavaScript调试技巧
    linux下出现ping:unknown host www.baidu.com问题时的解决办法——ubuntu下局域网络的配置
    linux网络配置相关命令、虚拟网络接口eth0:0
    网络游戏服务器架构设计
    Linux 下使用静态google protocl buffer
    php-fpm nginx 使用 curl 请求 https 出现 502 错误
  • 原文地址:https://www.cnblogs.com/jinkun113/p/12301930.html
Copyright © 2011-2022 走看看