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

    20200204 第一场

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

    A、honoka和格点三角形

    1、链接

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

    2、题面

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

    3、思路

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

    以底边垂直且长度为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 <bits/stdc++.h>
     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,获得a 分, (100−x)% 概率great,获得b 分。
    已知一首歌有n 个音符。kotori想知道,不考虑连击加成的话,一首歌得分的期望是多少?

    3、思路

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

    4、代码

     1 #include <bits/stdc++.h>
     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轴挡板可以挡该靶子。

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

    4、代码

     1 #include <bits/stdc++.h>
     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很喜欢吃米饭。
    有一天,她拿出了 n 个碗,第一个碗装了 1 粒米饭,第二个碗装了 2 粒米饭,以此类推,第 n 个碗装了 n 粒米饭。
    然而,爱搞恶作剧的rin把所有的碗的顺序打乱,并拿走了一个碗。hanayo想知道,rin拿走的碗里有多少粒米饭?

    3、思路

    签到题,略。

    4、代码

     1 #include <bits/stdc++.h>
     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,然后就巴拉巴拉弄了好久,人傻了。最近码力明显下滑了。

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

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

    4、代码

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

    3、思路

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

    4、代码

     1 #include <bits/stdc++.h>
     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有最多 k 次操作的机会。她想在操作之后找出一个尽可能长的连续子串,这个子串上的所有字符都相同。
    nozomi想问问聪明的你,这个子串的长度最大值是多少?
    注:k 次操作机会可以不全部用完。
    如果想知道连续子串的说明,可以去问问eli,nozomi不想再讲一遍。

    3、思路

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

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

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

    4、代码

     1 #include <bits/stdc++.h>
     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这东西好妙。可能我对抽象的东西接受得很慢吧。

    f[i]表示前i个字符的最大得分,状态由三种计分的字符串来转移,具体略。

    4、代码

     1 #include <bits/stdc++.h>
     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 }
  • 相关阅读:
    PHP用腾讯地图api根据地址获取经纬度的方法
    Tomcat实现负载均衡
    命令行备忘神器 Navi
    Bashtop – Linux的资源监视工具
    使用jinfo出现“can't determine target's VM version”问题解决方法
    axios 封装【满足常规数据上传及file上传】
    前端基础之设计模式篇
    使用Chrome插件拦截广告的原理
    前端基础之算法篇
    一道面试题引发的思考
  • 原文地址:https://www.cnblogs.com/jinkun113/p/12285921.html
Copyright © 2011-2022 走看看