[Nowcoder]2020牛客寒假算法基础集训营题集

一、20200208 第一场

进度(9/10) 未完成:J

A、honoka和格点三角形

1、链接

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

2、题面

honoka最近在研究三角形计数问题。
她认为,满足以下三个条件的三角形是“好三角形”。
1.三角形的三个顶点均为格点,即横坐标和纵坐标均为整数。
2.三角形的面积为 。
3.三角形至少有一条边和 轴或 轴平行。
honoka想知道,在平面中选取一个大小为 的矩形格点阵,可以找到多少个不同的“好三角形”?由于答案可能过大,请对 取模。

3、思路

根据题目限制条件,这样的三角形可以分类为:底边水平,底边垂直;每一类底边可以为1也可以为2,所以总共为4类。

[Nowcoder]2020牛客寒假算法基础集训营题集_第1张图片

以底边垂直且长度为1为例,如图所示,对于n * m的矩形格点阵,存在(n - 1) * m个这样的底边;对于每一个这样的底边,可以找出n个向右延伸的三角形。排除最靠右的两列,总共n * (m - 2) * (n - 1),同理加上向左延伸的三角形,共有2 * (n - 1) * (m - 2) * n个此类型三角形。

同理,底边垂直长度为2的三角形有2 * (n - 2) * (m - 1) * n个;

底边水平长度为1的三角形也有2 * (m - 1) * (n - 2) * m个,但是由于其中的直角三角形在之前已经被计算过了,所以要稍微修改一下:(m - 1) * (n - 2) * (2 * m - 4)

同理,底边水平长度为2的三角形有(m - 2) * (n - 1) * (2 * m - 4)个。

答案就是把上述四种三角形加起来。

到现在这道题就做完了,但由于n, m <= 10 ^ 9,计算时超出long long范围,直接相加会溢出(我懒得加了然后就WA了),所以要化成一个合适的形式,因式分解一下就行了。

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 #define MOD 1000000007
 5 typedef long long ll;
 6 
 7 ll n, m;
 8 
 9 int main() { 
10     cin >> n >> m;
11     cout << ((2 * m + 2 * n - 4) % MOD) * ((2 * n * m - 3 * n - 3 * m + 4) % MOD) % MOD;
12     return 0;
13 }

 

B、kotori和bangdream

1、链接

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

2、题面

有一天,kotori发现了一个和lovelive相似的游戏:bangdream。令她惊讶的是,这个游戏和lovelive居然是同一个公司出的!
kotori经过一段时间的练习后已经变得非常触,每个音符 x% 的概率perfect,获得 分, (100−x)% 概率great,获得 分。
已知一首歌有 个音符。kotori想知道,不考虑连击加成的话,一首歌得分的期望是多少?

3、思路

签到题。思路略。这里学到了一个cout保留小数的方法: cout << fixed <<  setprecision(x) << ...,x表示保留小数位数。

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 double n, x, a, b;
 5 
 6 int main() { 
 7     cin >> n >> x >> a >> b;
 8     cout << fixed << setprecision(2) << n * (x / 100 * a + (1 - x / 100) * b);
 9     return 0;
10 }

 

C、umi和弓道

1、链接

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

2、题面

umi对弓道非常痴迷。
有一天,她在研究一个射箭问题:
在一个无限大的平面中,她站在  这个坐标。
有  个靶子,第  个靶子的坐标是 
umi准备在  轴或  轴上放置一块挡板来挡住弓箭的轨迹,使得她可以射中的靶子数量不超过  个。
她想知道挡板的最短长度是多少?
注:假定弓箭的轨迹是起点为umi坐标、长度无穷大的射线。umi和靶子的体积可以无视。挡板的边缘碰到弓箭轨迹也可视为挡住弓箭。
注2:挡板不能弯折,起始和终点必须在同一坐标轴上。

3、思路

简单计算几何。当且仅当射的靶子和(x0, y0)不在同一象限,才可能被挡住。挡板要么在x轴,要么在y轴,则分类讨论:

① 靶子与(x0, y0)在x轴两侧,则x轴挡板可以挡该靶子;

② 靶子与(x0, y0)在y轴两侧,则y轴挡板可以挡该靶子。

[Nowcoder]2020牛客寒假算法基础集训营题集_第2张图片

利用计算几何知识,求出两种情况下两点所在直线与x/y轴相交的位置,并进行排序,然后双指针从左至右进行扫描,找到最小值即可。

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 #define MAXN 100005
 5 #define INF 0x3f3f3f3f
 6 
 7 double a, b, X, Y;
 8 int n, k, tx, ty;
 9 double xl[MAXN], yl[MAXN], xx[MAXN], xy[MAXN], yx[MAXN], yy[MAXN], ans = INF;
10 
11 int main() {
12     cin >> X >> Y >> n >> k;
13     for (int i = 1; i <= n; i++) {
14         cin >> a >> b;
15         if (a * X < 0 || a == 0) yx[++ty] = a, yy[ty] = b;
16         if (b * Y < 0 || b == 0) xx[++tx] = a, xy[tx] = b;
17     }
18     for (int i = 1; i <= tx; i++)
19         xl[i] = (xx[i] * Y - xy[i] * X) / (Y - xy[i]);
20     for (int i = 1; i <= ty; i++)
21         yl[i] = (yy[i] * X - yx[i] * Y) / (X - yx[i]);
22     sort(xl + 1, xl + tx + 1), sort(yl + 1, yl + ty + 1);
23     for (int i = 1; i <= tx - n + k + 1; i++) ans = min(ans, xl[i + n - k - 1] - xl[i]);
24     for (int i = 1; i <= ty - n + k + 1; i++) ans = min(ans, yl[i + n - k - 1] - yl[i]);
25     if (ans == INF) cout << -1;
26     else printf("%.8lf", ans);
27     return 0;
28 }

 

D、hanayo和米饭

1、链接

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

2、题面

hanayo很喜欢吃米饭。
有一天,她拿出了 个碗,第一个碗装了 粒米饭,第二个碗装了 粒米饭,以此类推,第 个碗装了 粒米饭。
然而,爱搞恶作剧的rin把所有的碗的顺序打乱,并拿走了一个碗。hanayo想知道,rin拿走的碗里有多少粒米饭?

3、思路

签到题,略。

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 
 6 ll n, o, tot;
 7 
 8 int main() { 
 9     cin >> n;
10     for (int i = 1; i <= n - 1; i++) cin >> o, tot += o;
11     cout << (1 + n) * n / 2 - tot;
12     return 0;
13 }

 

E、rin和快速迭代

1、链接

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

2、题面

rin最近喜欢上了数论。
然而数论实在太复杂了,她只能研究一些简单的问题。
这天,她在研究正整数因子个数的时候,想到了一个“快速迭代”算法。设 为 的因子个数,将 迭代下去,rin猜想任意正整数最终都会变成 。
例如: 。
她希望你帮她验证一下。她会给你一个正整数 ,让你输出它在迭代过程中,第一次迭代成 的迭代次数。

3、思路

(本来这也是个签到题吧。。结果我弄了大半天,烦)

虽然数据看着很大,n <= 10 ^ 12,但众所周知求n的约数,只需枚举1到sqrt(n),又根据题意的迭代方式,实际复杂度仅仅只有O(sqrt(n)) = O(10 ^ 6)。然而我首先没注意到如果它是完全平方数的情况,直接枚举完*2,然后就巴拉巴拉弄了好久,人傻了。最近码力明显下滑了。

不过这题又非暴力解法,由“约数个数定理”得:

[Nowcoder]2020牛客寒假算法基础集训营题集_第3张图片

这样就有了另一种思路,按下不表。

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 
 6 ll n;
 7 
 8 ll work(ll o, ll d) {
 9     if (o == 2) return d;
10     ll x = 0;
11     for (ll i = 1; i <= sqrt(o); i++) x += o % i ? 0 : i * i == o ? 1 : 2;
12     return work(x, d + 1);
13 }
14 
15 int main() {
16     cin >> n;
17     cout << work(n, 0);
18     return 0;
19 }

 

F、maki和tree

1、链接

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

2、题面

有一天,maki拿到了一颗树。所谓树,即没有自环、重边和回路的无向连通图。
这个树有 个顶点, 条边。每个顶点被染成了白色或者黑色。
maki想知道,取两个不同的点,它们的简单路径上有且仅有一个黑色点的取法有多少?
注:
①树上两点简单路径指连接两点的最短路。
② 和 的取法视为同一种。

3、思路

对于每一个黑点,所有与其相连的白色节点连通图相互之间的点可以取路径,取法个数为

 

可以简化为,以降低复杂度。

 

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 #define MAXN 100005
 5 #define INF 1 << 30
 6 
 7 typedef long long ll;
 8 
 9 char s[MAXN];
10 std :: vector <int> G[MAXN];
11 ll n, sum, x, y, ans;
12 
13 void dfs(int o, int f) {
14     if (s[o] == 'B') return;
15     sum++;
16     for (int i = 0; i < G[o].size(); i++)
17         if (G[o][i] != f) dfs(G[o][i], o);
18 }
19 
20 int main() {
21     cin >> n >> s + 1;
22     for (int i = 1; i < n; i++) {
23         cin >> x >> y;
24         G[x].push_back(y);
25         G[y].push_back(x);
26     }
27     for (int i = 1; i <= n; i++) {
28         ll t1 = 0, t2 = 0;
29         if (s[i] == 'B') {
30             for (int j = 0; j < G[i].size(); j++) {
31                 sum = 0, dfs(G[i][j], G[i][j]);
32                 ans += sum, t1 += sum, t2 += sum * sum;
33             }
34             ans += (t1 * t1 - t2) / 2;
35         }
36     }
37     cout << ans;
38     return 0;
39 }

 

G、rin和快速迭代

1、链接

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

2、题面

eli拿到了一个仅由小写字母组成的字符串。
她想截取一段连续子串,这个子串包含至少 个相同的某个字母
她想知道,子串的长度最小值是多少?
注:所谓连续子串,指字符串删除头部和尾部的部分字符(也可以不删除)剩下的字符串。例如:对于字符串“arcaea”而言,“arc””、“rcae”都是其子串。而car”、“aa”则不是它的子串。

3、思路

直接对于每个字母记录每一次出现的位置,再枚举距离为k时两个位置差的最小值。

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 #define MAXN 200005
 5 #define INF 1 << 30
 6 
 7 int a[205][MAXN], ans = INF, n, k;
 8 char ch[MAXN];
 9 
10 int main() { 
11     cin >> n >> k >> ch + 1;
12     for (int i = 1; i <= n; i++) a[ch[i]][++a[ch[i]][0]] = i;
13     for (int i = 'a'; i <= 'z'; i++) {
14         if (!a[i][0]) continue;
15         for (int j = 1; j <= a[i][0] - k + 1; j++) ans = min(ans, a[i][j + k - 1] - a[i][j]);
16     }
17     cout << (ans == INF ? -1 : ans + 1);
18     return 0;
19 }

 

H、nozomi和字符串

1、链接

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

2、题面

nozomi看到eli在字符串的“花园”里迷路了,决定也去研究字符串问题。
她想到了这样一个问题:
对于一个 01”串而言,每次操作可以把 0 字符改为 1 字符,或者把 1 字符改为 0 字符。所谓01”串,即只含字符 0 和字符 1 的字符串。
nozomi有最多 次操作的机会。她想在操作之后找出一个尽可能长的连续子串,这个子串上的所有字符都相同。
nozomi想问问聪明的你,这个子串的长度最大值是多少?
注: 次操作机会可以不全部用完。
如果想知道连续子串的说明,可以去问问eli,nozomi不想再讲一遍。

3、思路

自己做的时候思路爆炸了,知道是要用个l标记和r标记,来动态调整这个长度,然后在调整过程中记录最大值,写了许久却写不好。看了个题解豁然开朗。

题解给了个专业的词来表示这个方法——尺取法。在丈量一段区间是否合适时,秉承的规则是,在操作次数不超过限制的前提下尽可能区间尽可能长。

根据题意,我们有2种操作的方式——0换成1,1换成0。以0换成1为例,对于下一位为1,直接右移右端点;如果为0,当前还有操作次数,则消耗一个操作次数,右移右端点;如果没有了,则右移左端点直到为0,再右移右端点,以腾出一个操作次数使区间前进来寻找是否存在更大值。

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 #define MAXN 200005
 5 
 6 int n, k, ans;
 7 char ch[MAXN];
 8 
 9 int main() { 
10     cin >> n >> k >> ch + 1;
11     for (int i = 1; i <= n; i++) ch[i] -= '0';
12     for (int x = 0; x <= 1; x++) {
13         int l = 1, r = 1, o = 0;
14         for (int i = 1; i <= n; i++) {
15             if (ch[i] == 0)
16                 if (o < k) o++, r++;
17                 else {
18                     while (l <= r && ch[l] == 1) l++;
19                     l++, r++;
20                 }
21             else r++;
22             ans = max(ans, r - l);
23         }
24     }
25     cout << ans;
26     return 0;
27 }

 

I、nico和niconiconi

1、链接

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

2、题面

nico平时最喜欢说的口头禅是niconiconi~。
有一天nico在逛著名弹幕网站"niconico"的时候惊异的发现,n站上居然有很多她的鬼畜视频。其中有一个名为《让nico为你洗脑》的视频吸引了她的注意。
她点进去一看,就被洗脑了:"niconicoh0niconico*^vvniconicoG(vniconiconiconiconiconicoG(vniconico......"
弹幕中刚开始有很多“nico*1 nico*2”等计数菌,但到后面基本上都是“计数菌阵亡”的弹幕了。
nico也想当一回计数菌。她认为:"nico" 计 分,"niconi" 计 分,"niconiconi" 计 分。
她拿到了一个长度为 的字符串,请帮她算出最大计数分数。
注:已被计数过的字符不能重复计数!如"niconico"要么当作"nico"+"nico"计 分,要么当作"niconi"+"co"计 分。

3、思路

简单DP。小时候的我超不理解DP,现在觉得DP这东西好妙。可能我对抽象的东西接受得很慢吧。

状态由三种计分的字符串来转移,具体略。

4、代码

 1 #include 
 2 using namespace std;
 3 
 4 #define MAXN 300005
 5 typedef long long ll;
 6 
 7 const char s[4][10] = {{}, {'n', 'i', 'c', 'o'}, {'n', 'i', 'c', 'o', 'n', 'i'}, {'n', 'i', 'c', 'o', 'n', 'i', 'c', 'o', 'n', 'i'}};
 8 const ll l[4] = {0, 4, 6, 10};
 9 
10 ll n, p[4], f[MAXN];
11 char ch[MAXN];
12 
13 bool chk(ll o, ll x) {
14     if (o < 0) return 0;
15     for (ll i = o; i <= o + l[x] - 1; i++)
16         if (ch[i] != s[x][i - o]) return 0;
17     return 1;
18 }
19 
20 int main() { 
21     cin >> n >> p[1] >> p[2] >> p[3] >> ch + 1;
22     for (ll i = 1; i <= n; i++) {
23         for (ll j = 1; j <= 3; j++)
24             if (chk(i - l[j] + 1, j)) f[i] = max(f[i], f[i - l[j] + 1] + p[j]);
25         f[i] = max(f[i], f[i - 1]);
26     }
27     cout << f[n];
28     return 0;
29 }

 

持续更新中。。。

你可能感兴趣的:([Nowcoder]2020牛客寒假算法基础集训营题集)