作业链接 :https://www.luogu.com.cn/contest/314407
邀请码 :微信群通知
难度 :普及-
赛时答疑与赛后题解 :线下答疑
A. [NOIP 2008 普及组] ISBN 号码 [NOIP 2008 普及组] ISBN 号码
思路 简单模拟。
依次提取字符串中的前 9 位数字,按规则计算加权和并对 11 取模得出正确的识别码。将其与输入的最后一位字符进行比对,相等则输出 “Right”,否则替换最后一位并输出正确的字符串。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <bits/stdc++.h> using namespace std;char num[] = {'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'X' }; string s;int ans, code;int main () { cin >> s; int j = 0 ; for (int i = 0 ; i < 9 ; i ++) { if (s[j] == '-' ) j ++; ans += ((s[j ++] - '0' ) * (i + 1 )) % 11 ; ans %= 11 ; } for (int i = 0 ; i < 11 ; i ++) if (num[i] == s[12 ]) code = i; if (ans == code) cout << "Right" << endl; else { s[12 ] = num[ans]; cout << s << endl; } return 0 ; }
B. 逛画展 逛画展
方法一:暴力枚举(60分) 思路 双重循环暴力枚举区间的起点和终点,用数组统计当前区间内的画师种类。配合两个关键剪枝:当剩余画作总数不足 m 时直接结束外层循环,以及一旦找齐 m 位画师就更新答案并立刻跳出内层循环。时间复杂度O (n 2 ) 。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <bits/stdc++.h> using namespace std;const int N = 1e6 + 10 ; const int M = 2e3 = 5 ;int n, m, min_price, x, y;int a[N], cnt[M];int main () { min_price = 1e9 ; cin >> n >> m; for (int i = 1 ; i <= n; i ++) cin >> a[i]; for (int i = 1 ; i <= n; i ++) { if (n - i + 1 < m) break ; int types = 0 ; for (int k = 1 ; k <= m; k ++) cnt[k] = 0 ; for (int j = i; j <= n; j ++) { if (cnt[a[j]] == 0 ) types ++; cnt[a[j]] ++; if (types == m) { if (min_price > j - i + 1 ) { min_price = j - i + 1 ; x = i; y = j; } break ; } } } cout << x << ' ' << y << endl; return 0 ; }
方法二:双指针(滑动窗口) 思路 利用双指针维护一个滑动窗口,右指针不断向右扩展直到凑齐 m 位画师,然后左指针尝试向右收缩来寻找更短的合法区间。i、j指针实际上先后对同一个数组进行了一次遍历,时间复杂度为O(n)。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 #include <bits/stdc++.h> using namespace std;const int N = 1e6 + 10 ;const int M = 2e3 + 5 ;int n, m, min_price, x, y, types;int a[N], cnt[M];int main () { min_price = 1e9 ; cin >> n >> m; for (int i = 0 ; i < n; i ++) cin >> a[i]; int j = 0 ; for (int i = 0 ; i < n; i ++) { while (j < n && types < m) { if (cnt[a[j]] == 0 ) types ++; cnt[a[j]] ++; j ++; } if (types < m) break ; if (min_price > j - i) { min_price = j - i; x = i + 1 ; y = j; } cnt[a[i]] --; if (cnt[a[i]] == 0 ) types --; } cout << x << ' ' << y << endl; return 0 ; }
方法三:队列 思路 用队列模拟滑动窗口,将画作依次入队并用数组统计频次。如果队头画作的画师在队列中出现次数大于 1,说明队头元素是多余的,直接将其不断出队来极限压缩区间长度,从而实现 O (n ) 高效求解。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <bits/stdc++.h> using namespace std;const int N = 1e6 + 10 ;const int M = 2e3 + 5 ;int n, m, min_price, x, y, types;int a[N], cnt[M]; queue<int > q; int main () { min_price = 1e9 ; cin >> n >> m; for (int i = 1 ; i <= n; i ++) cin >> a[i]; for (int i = 1 ; i <= n; i ++) { q.push (i); if (cnt[a[i]] == 0 ) types ++; cnt[a[i]] ++; while (!q.empty () && cnt[a[q.front ()]] > 1 ) { cnt[a[q.front ()]] --; q.pop (); } if (types == m) { if (min_price > i - q.front () + 1 ) { min_price = i - q.front () + 1 ; x = q.front (); y = i; } } } cout << x << ' ' << y << endl; return 0 ; }
C. [CSP-S 2020] 儒略日 [CSP-S 2020] 儒略日
【数据范围】
测试点编号 Q =r i ≤1 1000 365 2 1000 104 3 1000 105 4 10000 3 × 105 5 10000 2.5 × 106 6 105 2.5 × 106 7 105 5 × 106 8 105 107 9 105 109 10 105 年份答案不超过 109
10分做法 思路 r i ≤ 365 ,一年内,枚举判断给定的天数落在几月几日即可。因为 4713≡1 (mod 4) ,而公元前1年为闰年,所以4713年为闰年。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <bits/stdc++.h> using namespace std;int month_days[] = {0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 };int q;long long r;int main () { cin >> q; while (q --) { cin >> r; r ++; month_days[2 ] ++; long long sum_days = 0 , target_month; for (int i = 1 ; i <= 12 ; i ++) { sum_days += month_days[i]; if (sum_days >= r) { target_month = i; r -= (sum_days - month_days[i]); printf ("%lld %lld 4713 BC\n" , r, target_month); break ; } } month_days[2 ] --; } return 0 ; }
40分做法 思路 r i ≤ 3 * 105 ,而1582年10月4日是2299160天,仅计算儒略历即可。引入年份计算,4年一个闰年,所以以4年为周期计算年份。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <bits/stdc++.h> using namespace std;int month_days[] = {0 , 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 };int q;long long r, year, is_leap;int main () { cin >> q; while (q --) { cin >> r; year = r / (365 * 4 + 1 ) * 4 ; r %= 365 * 4 + 1 ; is_leap = 1 ; if (r >= 366 ) { year += (r - 1 ) / 365 ; r = (r - 1 ) % 365 + 1 ; is_leap = 0 ; } if (is_leap == 1 ) r ++; if (is_leap == 1 ) month_days[2 ] ++; long long sum_days = 0 , target_month; for (int i = 1 ; i <= 12 ; i ++) { sum_days += month_days[i]; if (sum_days >= r) { target_month = i; r -= (sum_days - month_days[i]); if (year - 4713 < 0 ) printf ("%lld %lld %lld BC\n" , r, target_month, 4713 - year); else printf ("%lld %lld %lld\n" , r, target_month, year - 4712 ); break ; } } if (is_leap == 1 ) month_days[2 ] --; } return 0 ; }