[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

原文地址:https://www.cnblogs.com/jinkun113/p/12301930.html