A. 统计数字(数位DP)
暴力的从1到n每个数字处理一遍是非常慢的,通过规律来计算得解可以大大优化。
例如n = 465的情况:
首先不考虑去掉前导0,那么从00到99,0~9每个数字出现的次数一是一样的,一共有100 *2个数次,平摊到每个数字则是都出现了20次;
同理,100到199,除了1多出现100次以外,0 2 3 4 5 6 7 8 9又出现了20次;
同理200~299,300~399;
400之后,不能再统计到499,统计的规模要缩小,换成400~409,410~419,420~429,…,450~459;
接着考虑460~465。
然后要去掉多余的前导零:
首先000有一个,然后001~009有9个,010~099有90个,再往后没有前导零了。
#include <algorithm> #include <iostream> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <math.h> using namespace std; int d[10]; int cnt[10]; int getdigit(int n) { int ret = 0; while(n) { d[ret] = n % 10; n /= 10; ret++; } return ret; } int ten_pow(int k) { int ret = 1; while(--k) { ret *= 10; } return ret; } void getans(int n) { memset(cnt, 0, sizeof(cnt)); int len = getdigit(n); for(int p = len - 1; p > 0; p--) { int k = ten_pow(p); for(int i = 0; i < 10; i++) cnt[i] += k * d[p] * p; for(int i = len - 1; i > p; i--) cnt[d[i]] += k * 10 * d[p]; for(int i = 0; i < d[p]; i++) cnt[i] += k * 10; // for(int i = 0; i < 10; i++) printf("%d ", cnt[i]); cout << endl; } int w = n % 10; for(int i = 0; i <= w; i++) cnt[i]++; for(int i = len - 1; i > 0; i--) cnt[d[i]] += w + 1; cnt[0] -= len; int t = len, s = 9; while(t--) { cnt[0] -= t * s; s *= 10; } for(int i = 0; i < 10; i++) printf("%d\n", cnt[i]); } int main() { // freopen("13124.in", "r", stdin); int n; while(~scanf("%d", &n)) { getans(n); } return 0; }
B. 青姬の爱恋
素数打表到maxn^(1/2),对于每个数n,找到较小的素数p,输出n / p。
#include <algorithm> #include <iostream> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <math.h> using namespace std; const int maxn = 1e5; const int prin = 5e4; int prime[prin]; ///仅包含奇数,isprime[k]代表2*k+3 bool isprime[maxn >> 1]; int ptop = 0; void sieve() { memset(isprime, true, sizeof(isprime)); prime[0] = 2; int sq = sqrt(maxn); for(int i = 3; i < maxn; i += 2) if(isprime[(i-3) >> 1]) { if(i <= sq + 3) { for(int j = i * i; j < maxn; j += 2 * i) isprime[(j-3) >> 1] = false; } prime[++ptop] = i; } } int main() { // freopen("13125.in", "r", stdin); sieve(); int n; while(~scanf("%d", &n)) { int k = 0; while(n % prime[k] != 0 && k < ptop) { k++; } printf("%d\n", n / prime[k]); } return 0; }
C. 买水果
整数规划,更简便的计算方法是用到五边形数定理。
#include <stdio.h> #include <string.h> int p[110]; void init() { p[0] = p[1] = 1; p[2] = 2; p[3] = 3; for(int i = 4; i < 110; i++) { p[i] = 0; int flag = 1; for(int j = 1; ; j++) { int p1 = j * (3 * j - 1) / 2; int p2 = j * (3 * j + 1) / 2; if(p1 > i && p2 > i) break; if(p1 <= i) p[i] = p[i] + flag * p[i - p1]; if(p2 <= i) p[i] = p[i] + flag * p[i - p2]; flag *= (-1); } } } int main() { init(); int t, n; while(scanf("%d", &n) != EOF) { printf("%d\n", p[n]); } }
D. 小明和小红
Nim博弈类问题。在N符合条件的情况下,先手有必赢的策略。
假设N模4不为0,先手第一轮拿走N % 4颗糖果,后手每次拿x颗糖果,先手都拿4 – x颗,后手必输;
若N模4为0,先手必输。
E. 离散函数
绿色的线条表示非最优解,蓝色线条表示非法解,红色线条表示最优解。
对于每个点,一定是和它的相邻一个点的连线是最优解,所以只需要找相邻两个点的斜率最大值即可。
F. 熊大的学籍管理系统(hash)
将字符串通过hash函数处理成一个数,如果出现不同的字符串hash后得到同一个hash值,则用链表储存下来,查询的时候,只需搜该hash值对应的所有字符串。
这个题还可以用c++里stl中的map来做,非常方便。
#include <iostream> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <math.h> using namespace std; #define HASH 20011 typedef char Str[12]; Str id[HASH], na[HASH]; int n, head[HASH], next[HASH]; inline int BKDR_hash(char *str) { int seed = 131; int hash = 0; while(*str) hash = hash * seed + (*str++); return (hash & 0x7fffffff) % HASH; } inline int add_hash(int s) { int h = BKDR_hash(id[s]); next[s] = head[h]; head[h] = s; return 1; } inline int srch(char *str) { int h = BKDR_hash(str); int u = head[h]; while(u) { if(strcmp(id[u], str) == 0) return u; u = next[u]; } return -1; } Str idtmp, natmp, quary; int main() { // freopen("13129.in", "r", stdin); int N, Q, k; while(scanf("%d", &N) != EOF) { memset(id, 0, sizeof(id)); memset(na, 0, sizeof(na)); memset(head, 0, sizeof(head)); memset(next, 0, sizeof(next)); k = 1; for(int i = 0; i < N; i++) { scanf("%s%s", idtmp, natmp); if(srch(idtmp) != -1) printf("%s has existed!\n", idtmp); else { memcpy(id[k], idtmp, sizeof(id[k])); memcpy(na[k], natmp, sizeof(na[k])); add_hash(k); k++; } } scanf("%d", &Q); for(int i = 0; i < Q; i++) { scanf("%s", quary); int w = srch(quary); if(w == -1) printf("Can not find %s!\n", quary); else printf("%s\n", na[w]); } } return 0; }
G. 她们会去哪里(几何)
纯计算。
H. The Princess Number(暴力)
数据范围不够大,可以暴力做。数据范围大的时候,先对所有数排序,再进行统计即可。
#include <algorithm> #include <iostream> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <math.h> using namespace std; #define MAXN 1000100 int cnt[MAXN], a[MAXN], na[MAXN], n; int ans[MAXN], maxc, maxa, tot; int l(int ai) { return lower_bound(a, a + n, ai) - a; } int r(int ai) { return upper_bound(a, a + n, ai) - a; } int main() { int cas = 1; while(~scanf("%d", &n)) { for(int i = 0; i < n; i++) scanf("%d", a + i); sort(a, a + n); maxa = a[n - 1]; int now = 0; na[0] = a[0]; for(int i = 0; i < n; i++) if(na[now] != a[i]) na[++now] = a[i]; now++; maxc = 0; memset(cnt, 0, sizeof(cnt)); for(int i = 0; i < now; i++) { cnt[na[i]] = r(na[i]) - l(na[i]); maxc = max(maxc, cnt[na[i]]); } tot = 0; for(int i = 0; i <= maxa; i++) if(cnt[i] == maxc) ans[tot++] = i; printf("Case #%d: %d\n", cas++, tot); for(int i = 0; i < tot; i++) { if(i) printf(" "); printf("%d", ans[i]); } printf("\n"); } return 0; }