甜甜圈战役

· · 个人记录

一场 【NOIP2012 普及组】 的重现赛,成绩好的有甜甜圈

于是这场比赛的名字出现了……

甜甜圈-质因数分解

题目描述

已知正整数 n 是两个不同的质数的乘积,试求出较大的那个质数。

输入格式

输入只有一行,包含一个正整数 n

输出格式

输出只有一行,包含一个正整数 p,即较大的那个质数

样例 #1

样例输入 #1

21

样例输出 #1

7

提示

对于60\%的数据,6 \leqslant n \leqslant 1000

对于100\%的数据,6 \leqslant n\leqslant 2×10^9

原代码

# include <bits/stdc++.h>
# define int long long

using namespace std;

int n, p = INT_MIN;

bool prime(int x)
{

    if (x < 2) return 0;
    if (x == 2) return 1;
    for (int i = 2; i * i <= x; i++) if (x % i == 0) return 0;
    return 1;

}

signed main()
{

    cin >> n;
    for (int i = 1; i <= n; i++)
        if (n % i == 0 && prime(i)) p = i;
    cout << p; 

    return 0;

}

此题……分外的水啊……

我直接写上质数暴力判断,然后一个个因数检查至最大

但我还是 WA 了……

查了查,TLE 了 4 个点

一个个输入及其的大,O(n) 的时间复杂度根本吃不消

结果看题时猛然发现……

已知正整数 n 是两个不同的质数的乘积

那还判个啥,输出就完了

修改后

# include <bits/stdc++.h>
# define int long long

using namespace std;

int n, p = INT_MIN;

bool prime(int x)
{

    if (x < 2) return 0;
    if (x == 2) return 1;
    for (int i = 2; i * i <= x; i++) if (x % i == 0) return 0;
    return 1;

}

signed main()
{

    cin >> n;
    for (int i = 1; i <= n; i++)
        if (n % i == 0 && prime(i))
        {
            cout << p;
            break;
        }

    return 0;

}

但我再仔细一看,我又发现,还能再优化:

因为 n 一定是两个质数的乘积

所以只要从 2 判断到 \sqrt{n} 即可

优化后

# include <bits/stdc++.h>

using namespace std;

int n;

int main()
{

    cin >> n;
    for (int i = 2; i <= sqrt(n); i++)
        if (n % i == 0)
        {
            cout << n / i;
            break;
        }

    return 0;

}

甜甜圈-寻宝

题目背景

传说很遥远的藏宝楼顶层藏着诱人的宝藏。

题目描述

小明历尽千辛万苦终于找到传说中的这个藏宝楼,藏宝楼的门口竖着一个木板,上面写有几个大字:寻宝说明书。

说明书的内容如下:藏宝楼共有 N+1 层,最上面一层是顶层,顶层有一个房间里面藏着宝藏。除了顶层外,藏宝楼另有 N 层,每层 M 个房间,这 M 个房间围成一圈并按逆时针方向依次编号为 0,…,M-1。其中一些房间有通往上一层的楼梯,每层楼的楼梯设计可能不同。每个房间里有一个指示牌,指示牌上有一个数字 x,表示从这个房间开始按逆时针方向选择第 x 个有楼梯的房间(假定该房间的编号为 k),从该房间上楼,上楼后到达上一层的 k 号房间。比如当前房间的指示牌上写着 2,则按逆时针方向开始尝试,找到第 2 个有楼梯的房间,从该房间上楼。如果当前房间本身就有楼梯通向上层,该房间作为第一个有楼梯的房间。

寻宝说明书的最后用红色大号字体写着:“寻宝须知:帮助你找到每层上楼房间的指示牌上的数字(即每层第一个进入的房间内指示牌上的数字)总和为打开宝箱的密钥”

请帮助小明算出这个打开宝箱的密钥。

输入格式

第一行 2 个整数 NM,之间用一个空格隔开。N 表示除了顶层外藏宝楼共 N 层楼,M 表示除顶层外每层楼有M个房间。

接下来 N×M 行,每行两个整数,之间用一个空格隔开,每行描述一个房间内的情况,其中第 (i-1)×M+j 行表示第ij-1 号房间的情况(i=1, 2, …, Nj=1, 2, …,M)。第一个整数表示该房间是否有楼梯通往上一层(0 表示没有,1 表示有),第二个整数表示指示牌上的数字。注意,从 j 号房间的楼梯爬到上一层到达的房间一定也是 j 号房间。

最后一行,一个整数,表示小明从藏宝楼底层的几号房间进入开始寻宝(注:房间编号从 0 开始)。

输出格式

输出只有一行,一个整数,表示打开宝箱的密钥,这个数可能会很大,请输出对 20123 取模的结果即可。

样例 #1

样例输入 #1

2 3
1 2
0 3
1 4
0 1
1 5
1 2
1

样例输出 #1

5

提示

【输入输出样例说明】

第一层:

$1$号房间,无楼梯通往上层,指示牌上的数字是$3$; $2$号房间,有楼梯通往上层,指示牌上的数字是$4$; 第二层: $0$号房间,无楼梯通往上层,指示牌上的数字是$1$; $1$号房间,有楼梯通往上层,指示牌上的数字是$5$; $2$号房间,有楼梯通往上层,指示牌上的数字是$2$; 小明首先进入第一层(底层)的 $1$ 号房间,记下指示牌上的数字为 $3$,然后从这个房间开始,沿逆时针方向选择第 $3$ 个有楼梯的房间$2$号房间进入,上楼后到达第二层的 $2$ 号房间,记下指示牌上的数字为 $2$,由于当前房间本身有楼梯通向上层,该房间作为第一个有楼梯的房间。因此,此时沿逆时针方向选择第 $2$ 个有楼梯的房间即为 $1$ 号房间,进入后上楼梯到达顶层。这时把上述记下的指示牌上的数字加起来,即 $3+2=5$,所以打开宝箱的密钥就是 $5$。 【数据范围】 对于 $50\%$ 数据,有 $0<N\leqslant1000$,$0<x\leqslant10000$; 对于 $100\%$ 数据,有 $0<N\leqslant10000$,$0<M\leqslant100$,$0<x\leqslant1,000,000$。 ## 原代码 ```cpp # include <bits/stdc++.h> # define int long long using namespace std; int n, m, start, cnt, k, ans, mp[10005][105]; bool bol[10005][105]; signed main() { cin >> n >> m; for (int i = 1; i <= n; i++) for (int j = 0; j < m; j++) cin >> bol[i][j] >> mp[i][j]; cin >> start; cout << endl; k = start; for (int i = 1; i <= n; i++) { ans += mp[i][start]; while (cnt <= mp[i][start]) { if (k == m) k = 0; if (bol[i][k]) cnt++; cout << cnt << endl; if (cnt < mp[i][start]) k++; } start = k; cnt = 0; } cout << ans; return 0; } ``` 提交后我发现了一个令人脑抽的问题:我**测试代码没删,而且没有模……** 整体思路也算简单,一个数组 $bol_{i,j}$ **储存此房间是否有楼梯**,另一个数组 $mp_{i,j}$ **储存地图中的指示牌** 接着就是约瑟夫大模拟:用 $start$ **储存指示牌位置**,$k$ **储存小明的位置**,$ans$ **储存指示牌之和**,$cnt$ **储存上楼次数**,按照题意进行模拟 于是我修改了一下再提交,信心满满的我…… ## 修改后 ```cpp # include <bits/stdc++.h> # define int long long using namespace std; int n, m, start, k, ans, mp[10005][105]; bool bol[10005][105]; signed main() { cin >> n >> m; for (int i = 1; i <= n; i++) for (int j = 0; j < m; j++) cin >> bol[i][j] >> mp[i][j]; cin >> start; k = start; for (int i = 1; i <= n; i++) { ans += mp[i][start]; ans %= 20123; while (mp[i][start] > 0) { k++; k %= m; if (bol[i][k]) mp[i][start]--; } start = k; } cout << ans; return 0; } ``` 大大的 **WA + TLE** 写在测试点上 我仔细观察了一下,发现了一些问题: 1. **若 $mp_{i,start} = 0$,则直接上楼**,而我**没有特判** 2. 修改后的代码,**不会判断起始点有没有楼梯** ## 最后一个是最致命的 bug: **如果 $x$ 特别大,如每层都是 999999,我们的程序就要执行整整 99999900 次,这对 1 秒的时限是十分致命的!** 所以我们可以解决一下这个问题(另两个不做太多解释) 我们重新创建一个 $cnt_i$,储存**每层的楼梯数**,然后将 $mp_{i,start}$ **取模**,**节省大量时间** ## 新代码 ```cpp # include <bits/stdc++.h> # define int long long using namespace std; int n, m, start, cnt[10005], k, ans, mp[10005][105]; bool bol[10005][105]; signed main() { cin >> n >> m; for (int i = 1; i <= n; i++) for (int j = 0; j < m; j++) { cin >> bol[i][j] >> mp[i][j]; if (bol[i][j]) cnt[i]++; } cin >> start; for (int i = 1; i <= n; i++) { ans += mp[i][start]; ans %= 20123; k = start; mp[i][start] %= cnt[i]; if (!mp[i][start]) mp[i][start] = cnt[i]; if (bol[i][k]) mp[i][start]--; while (mp[i][start] > 0) { k++; k %= m; if (bol[i][k]) mp[i][start]--; } start = k; } cout << ans; return 0; } ```