[{"content":"质因数分解 已知正整数n是两个不同的质数的乘积，试求出较大的那个质数。\n题解 分解质因数模板题。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; int main() { int n, ans; cin \u0026gt;\u0026gt; n; for (int i = 2; i * i \u0026lt;= n; i++) { while (n % i == 0) { n /= i; ans = i; } } if (n \u0026gt; 1) { ans = n; } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; endl; } 寻宝 题面 传说很遥远的藏宝楼顶层藏着诱人的宝藏。小明历尽千辛万苦终于找到传说中的这个藏宝楼，藏宝楼的门口竖着一个木板，上面写有几个大字：寻宝说明书。说明书的内容如下：\n藏宝楼共有N+1层，最上面一层是顶层，顶层有一个房间里面藏着宝藏。除了顶层外，藏宝楼另有N层，每层M个房间，这M个房间围成一圈并按逆时针方向依次编号为0，…，M-1。其中一些房间有通往上一层的楼梯，每层楼的楼梯设计可能不同。每个房间里有一个指示牌，指示牌上有一个数字x，表示从这个房间开始按逆时针方向选择第x个有楼梯的房间（假定该房间的编号为k），从该房间上楼，上楼后到达上一层的k号房间。比如当前房间的指示牌上写着2，则按逆时针方向开始尝试，找到第2个有楼梯的房间，从该房间上楼。如果当前房间本身就有楼梯通向上层，该房间作为第一个有楼梯的房间。\n寻宝说明书的最后用红色大号字体写着：“寻宝须知：帮助你找到每层上楼房间的指示牌上的数字（即每层第一个进入的房间内指示牌上的数字）总和为打开宝箱的密钥”。\n请帮助小明算出这个打开宝箱的密钥。\n输入 第一行2个整数N和M，之间用一个空格隔开。N表示除了顶层外藏宝楼共N层楼，M表示除顶层外每层楼有M个房间。\n接下来N*M行，每行两个整数，之间用一个空格隔开，每行描述一个房间内的情况，其中第(i-1)*M+j行表示第i层j-1号房间的情况（i=1, 2, …, N；j=1, 2, … ,M）。第一个整数表示该房间是否有楼梯通往上一层（0表示没有，1表示有），第二个整数表示指示牌上的数字。注意，从j号房间的楼梯爬到上一层到达的房间一定也是j号房间。\n最后一行，一个整数，表示小明从藏宝楼底层的几号房间进入开始寻宝（注：房间编号从0开始）。\n输出 输出只有一行，一个整数，表示打开宝箱的密钥，这个数可能会很大，请输出对20123取模的结果即可。\n输入样例 1 2 3 1 2 0 3 1 4 0 1 1 5 1 2 1 输出样例 1 5 提示 【输入输出样例说明】 第一层：\n0号房间，有楼梯通往上层，指示牌上的数字是2；\n1号房间，无楼梯通往上层，指示牌上的数字是3；\n2号房间，有楼梯通往上层，指示牌上的数字是4；\n第二层：\n0号房间，无楼梯通往上层，指示牌上的数字是1；\n1号房间，有楼梯通往上层，指示牌上的数字是5；\n2号房间，有楼梯通往上层，指示牌上的数字是2；\n小明首先进入第一层（底层）的1号房间，记下指示牌上的数字为3，然后从这个房间开始，沿逆时针方向选择第3个有楼梯的房间2号房间进入，上楼后到达第二层的2号房间，记下指示牌上的数字为2，由于当前房间本身有楼梯通向上层，该房间作为第一个有楼梯的房间。因此，此时沿逆时针方向选择第2个有楼梯的房间即为1号房间，进入后上楼梯到达顶层。这时把上述记下的指示牌上的数字加起来，即3+2=5，所以打开宝箱的密钥就是5。\n题解 大模拟。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 10005, M = 105; int n, m; vector\u0026lt;int\u0026gt; a[N], b[N]; int c[N][M]; int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; for (int i = 1; i \u0026lt;= n; i++) { for (int j = 0; j \u0026lt; m; j++) { int s; scanf(\u0026#34;%d%d\u0026#34;, \u0026amp;s, \u0026amp;c[i][j]); if (s == 0) { a[i].push_back(j); } else { b[i].push_back(j); } } } int cur, ans = 0; cin \u0026gt;\u0026gt; cur; for(int i = 1;i \u0026lt;= n; i++) { auto it = lower_bound(a[i].begin(), a[i].end(), cur); int x = -1; if (it != a[i].end() \u0026amp;\u0026amp; *it == cur) { x = c[i][*it]; } it = lower_bound(b[i].begin(), b[i].end(), cur); if (it == b[i].end()) { it = b[i].begin(); } if (x \u0026lt; 0) { x = c[i][*it]; } int k = (it - b[i].begin() + x - 1) % b[i].size(); (ans += x) %= 20123; cur = b[i][k]; } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; endl; } 摆花 已知$n$，$m$，$a_i$，求$\\sum_{i=1}^{n}{x_i}=m (0\u0026lt;=x_i\u0026lt;=a_i)$的正整数解方案数。\n题解 递推，令$f[i][j]$表示前$i$个未知数的和为$j$的方案数。 $$f[i][j] = \\sum_{k=max(0, j-a[i])}^{j}{f[i-1][k]}$$ 时间复杂度$O(nm^2)$\n优化1 利用前缀和。 令 $$ s[i][j]=\\sum_{k=0}^{j}{f[i][k]} \\newline =\\sum_{k=0}^{j-1}{f[i][k]}+f[i][j] = s[i][j-1]+f[i][j] $$ 于是 $$ f[i][j] = \\begin{cases} s[i-1][j] \u0026amp;\\text{if } j\u0026lt;=a[i] \\newline s[i-1][j] - s[i-1][j-a[i]-1] \u0026amp;\\text{otherwise} \\end{cases} $$ 时间复杂度$O(nm)$\n拓展 当$n$较小时有用容斥原理+组合数的跟m无关的指数级算法。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 105, M = 1000007; int n, m, f[N][N], a[N], s[N][N]; int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; for (int i = 1; i \u0026lt;= n; i++) { cin \u0026gt;\u0026gt; a[i]; } f[0][0] = 1; for (int i = 0; i \u0026lt;= m; i++) { s[0][i] = 1; } for (int i = 1; i \u0026lt;= n; i++) { for (int j = 0; j \u0026lt;= m; j++) { if (j \u0026lt;= a[i]) { f[i][j] = s[i-1][j]; } else { f[i][j] = (s[i-1][j] - s[i-1][j-a[i]-1] + M) % M; } s[i][j] = (s[i][j-1] + f[i][j]) % M; } } cout \u0026lt;\u0026lt; f[n][m] \u0026lt;\u0026lt; endl; } 文化之旅 题面 有一位使者要游历各国，他每到一个国家，都能学到一种文化，但他不愿意学习任何一种文化超过一次（即如果他学习了某种文化，则他就不能到达其他有这种文化的国家）。不同的国家可能有相同的文化。不同文化的国家对其他文化的看法不同，有些文化会排斥外来文化（即如果他学习了某种文化，则他不能到达排斥这种文化的其他国家）。\n现给定各个国家间的地理关系，各个国家的文化，每种文化对其他文化的看法，以及这位使者游历的起点和终点（在起点和终点也会学习当地的文化），国家间的道路距离，试求从起点到终点最少需走多少路。\n输入 第一行为五个整数N，K，M，S，T，每两个整数之间用一个空格隔开，依次代表国家个数（国家编号为1到N），文化种数（文化编号为1到K），道路的条数，以及起点和终点的编号（保证S不等于T）；\n第二行为N个整数，每两个整数之间用一个空格隔开，其中第$i$个数$C_i$，表示国家$i$的文化为$C_i$。\n接下来的K行，每行K个整数，每两个整数之间用一个空格隔开，记第$i$行的第$j$个数为$a_{i,j}$，$a_{i,j}=1$表示文化$i$排斥外来文化$j$（$i$等于$j$时表示排斥相同文化的外来人），$a_{i,j}=0表示不排斥（注意$i$排斥$j$并不保证$j$一定也排斥$i$）。\n接下来的M行，每行三个整数$u,v,d$，每两个整数之间用一个空格隔开，表示国家$u$与国家$v$有一条距离为$d$的可双向通行的道路（保证$u$不等于$v$，两个国家之间可能有多条道路）\n输出 输出只有一行，一个整数，表示使者从起点国家到达终点国家最少需要走的距离数（如果无解则输出-1）。\n输入样例 1 2 2 1 1 2 1 2 0 1 1 0 1 2 10 输出样例 1 -1 输入样例 2 2 2 1 1 2 1 2 0 1 0 0 1 2 10 输出样例 2 10 提示 【输入输出样例说明1】\n由于到国家2必须要经过国家1，而国家2的文明却排斥国家1的文明，所以不可能到达国家2。\n【输入输出样例说明2】\n路线为1 -\u0026gt; 2。\n【数据范围】\n对于20%的数据，有2≤N≤8，K≤5；\n对于30%的数据，有2≤N≤10，K≤5；\n对于50%的数据，有2≤N≤20，K≤8；\n对于70%的数据，有2≤N≤100，K≤10；\n对于100%的数据，有2≤N≤100，1≤K≤100，1≤M≤N2，1≤ki≤K，1≤u, v≤N，1≤d≤1000，S≠T，1 ≤S, T≤N。\n题解 二分答案然后A*爆搜判断。\n估价函数为：当前走过的距离+当前国家到终点的不经过与已经走过的国家文化排斥的国家的最短路。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 105; int n, k, m, s, t, a[N][N], c[N], d[N][N], v[N], q[N * N], g[N]; int check(int y) { for (int x = 1; x \u0026lt;= n; x++) { if (v[x] \u0026amp;\u0026amp; a[c[y]][c[x]]) { return 0; } } return 1; } int cal(int x) { memset(g, 0x3f, sizeof(g)); q[1] = x; g[x] = 0; for (int l = 1, r = 1; l \u0026lt;= r; l++) { int x = q[l]; for (int y = 1; y \u0026lt;= n; y++) { if (g[x] + d[x][y] \u0026lt; g[y] \u0026amp;\u0026amp; check(y)) { g[y] = g[x] + d[x][y]; q[++r] = y; } } } return g[t]; } int dfs(int x, int dis, int lim) { if (dis + cal(x) \u0026gt; lim) { return 0; } if (x == t) { return 1; } v[x] = 1; for (int y = 1; y \u0026lt;= n; y++) { if (check(y) \u0026amp;\u0026amp; dfs(y, dis + d[x][y], lim)) { v[x] = 0; return 1; } } v[x] = 0; return 0; } int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; k \u0026gt;\u0026gt; m \u0026gt;\u0026gt; s \u0026gt;\u0026gt; t; for (int i = 1; i \u0026lt;= n; i++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;c[i]); } for (int i = 1; i \u0026lt;= k; i++) { for (int j = 1; j \u0026lt;= k; j++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i][j]); } a[i][i] = 1; } memset(d, 0x3f, sizeof(d)); for (int i = 1; i \u0026lt;= m; i++) { int u, v, x; scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;u, \u0026amp;v, \u0026amp;x); d[u][v] = d[v][u] = min(d[u][v], x); } int ans = -1; for (int l = 1, r = 1e5; l \u0026lt;= r;) { int m = (l + r) / 2; if (dfs(s, 0, m)) { r = m - 1; ans = m; } else { l = m + 1; } } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; endl; } ","date":"2021-09-13T16:58:00+08:00","permalink":"https://www.961996.xyz/p/noip2012%E6%99%AE%E5%8F%8A%E7%BB%84%E9%A2%98%E8%A7%A3/","title":"NOIP2012普及组题解"},{"content":"数字反转 将一个数反转之后输出，不能有前导零。\n题解 模拟。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; int n; int main() { cin \u0026gt;\u0026gt; n; int m = 0; int x = (n \u0026lt; 0); n = abs(n); while (n \u0026gt; 0) { m = m * 10 + n % 10; n /= 10; } if (x) { m = -m; } cout \u0026lt;\u0026lt; m \u0026lt;\u0026lt; endl; } 统计单词数 给一个单词和一段文章，输出单词在文章中出现的次数和第一次出现的位置，不区分大小写。\n题解 模拟。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 1e6+5; string s, p; int main() { getline(cin, s); getline(cin, p); s += \u0026#39; \u0026#39;; p += \u0026#39; \u0026#39;; for (int i = 0; i \u0026lt; s.length(); i++) { if (\u0026#39;A\u0026#39; \u0026lt;= s[i] \u0026amp;\u0026amp; s[i] \u0026lt;= \u0026#39;Z\u0026#39;) { s[i] = \u0026#39;a\u0026#39; + s[i] - \u0026#39;A\u0026#39;; } } for (int i = 0; i \u0026lt; p.length(); i++) { if (\u0026#39;A\u0026#39; \u0026lt;= p[i] \u0026amp;\u0026amp; p[i] \u0026lt;= \u0026#39;Z\u0026#39;) { p[i] = \u0026#39;a\u0026#39; + p[i] - \u0026#39;A\u0026#39;; } } int count = 0, pos = -1; for (int i = 0; i \u0026lt; p.length(); i++) { if ((i == 0 || p[i-1] == \u0026#39; \u0026#39;) \u0026amp;\u0026amp; p[i] != \u0026#39; \u0026#39;) { int flag = 1; for (int j = 0; j \u0026lt; s.length(); j++) { if (p[i + j] != s[j]) { flag = 0; break; } } count += flag; if (flag \u0026amp;\u0026amp; pos \u0026lt; 0) { pos = i; } } } if (pos \u0026gt;= 0) { cout \u0026lt;\u0026lt; count \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; pos \u0026lt;\u0026lt; endl; } else { cout \u0026lt;\u0026lt; -1 \u0026lt;\u0026lt; endl; } } 瑞士轮 给$2n$个整数对$(s_i,w_i)$（$w_i$唯一），有以下操作：\n先将整数对按照$s_i$从大到小排序，之后将$2k-1$，$2k$($1\u0026lt;=k\u0026lt;=n$)分为一组， 如果$w_{2k-1} \u0026lt; w_{2k}$，$s_{2k}$加1，否则$s_{2k-1}$加1。 问进行以上操作$R$次之后按照$s_i$排序的第$Q$个整数对在未操作之前的编号是多少。\n题解 直接暴力模拟的时间复杂度$O(nr\\log{n})$。\n每次操作之后将加1的分为一组，加0的分为一组，显然这样分组可以保证每组中依然是按照$s_i$排序的， 这样只要进行一次归并排序的合并操作，就能保证全部按照$s_i$排序。时间复制度$O(nr+n\\log{n})$。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 2e5 + 5; int n, r, q; struct P { int s, w, i; friend int operator\u0026lt;(P a, P b) { return a.s \u0026gt; b.s || a.s == b.s \u0026amp;\u0026amp; a.i \u0026lt; b.i; } } a[N], b[N]; int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; r \u0026gt;\u0026gt; q; for (int i = 1; i \u0026lt;= n * 2; i++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i].s); } for (int i = 1; i \u0026lt;= n * 2; i++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i].w); a[i].i = i; } sort(a + 1, a + n * 2 + 1); for (int i = 1; i \u0026lt;= r; i++) { for (int j = 1; j \u0026lt;= 2 * n; j += 2) { if (a[j].w \u0026gt; a[j + 1].w) { a[j].s += 1; } else { a[j + 1].s += 1; swap(a[j], a[j+1]); } } int m = 0; for (int j = 1, k = 2; j \u0026lt;= 2 * n || k \u0026lt;= 2 * n;) { if (k \u0026gt; 2 * n || j \u0026lt;= 2 * n \u0026amp;\u0026amp; a[j] \u0026lt; a[k]) { b[++m] = a[j]; j += 2; } else { b[++m] = a[k]; k += 2; } } for (int j = 1; j \u0026lt;= 2 * n; j++) { a[j] = b[j]; } } cout \u0026lt;\u0026lt; a[q].i \u0026lt;\u0026lt; endl; } 表达式的值 定义新运算 ×运算优先于⊕运算。 给一个由×⊕()组成的表达式，填入0或1使得成为一个合法的表达式，问有多少种填法使得最后算出的值为0。\n题解 先要会表达式求值。\n转换为后缀表达式之后dp，后缀表达式计算的过程就是dp的过程。\n每次取出栈顶的两个dp的状态进行运算，得到新的状态再放回栈顶。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 1e5 + 5; const int M = 10007; char s[N]; int n; stack\u0026lt;pair\u0026lt;int,char\u0026gt;\u0026gt; st; stack\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt; f; int main() { cin \u0026gt;\u0026gt; n; scanf(\u0026#34;%s\u0026#34;, s + 1); int cnt = 1; f.push(make_pair(1, 1)); int k = 0; s[n+1] = \u0026#39;)\u0026#39;; for (int i = 1; i \u0026lt;= n + 1; i++) { if (s[i] == \u0026#39;(\u0026#39;) { cnt += 1; continue; } int val = 0; if (s[i] == \u0026#39;*\u0026#39;) { val = cnt * 3 + 2; } if (s[i] == \u0026#39;+\u0026#39;) { val = cnt * 3 + 1; } if (s[i] == \u0026#39;)\u0026#39;) { val = cnt * 3; cnt -= 1; } while(!st.empty()\u0026amp;\u0026amp;st.top().first \u0026gt;= val) { pair\u0026lt;int,int\u0026gt; f1 = f.top(); f.pop(); pair\u0026lt;int,int\u0026gt; f2 = f.top(); f.pop(); pair\u0026lt;int,int\u0026gt; g; if (st.top().second == \u0026#39;+\u0026#39;) { g.first = f1.first * f2.first % M; g.second = (f1.first * f2.second + f1.second * f2.first + f1.second * f2.second) % M; } else { g.first = (f1.first * f2.first + f1.first * f2.second + f1.second * f2.first) % M; g.second = f1.second * f2.second % M; } f.push(g); st.pop(); } if (s[i] != \u0026#39;)\u0026#39;) { st.push(make_pair(val, s[i])); f.push(make_pair(1, 1)); } } cout \u0026lt;\u0026lt; f.top().first \u0026lt;\u0026lt; endl; } ","date":"2021-09-12T10:14:44+08:00","permalink":"https://www.961996.xyz/p/noip2011%E6%99%AE%E5%8F%8A%E7%BB%84%E9%A2%98%E8%A7%A3/","title":"NOIP2011普及组题解"},{"content":"数字统计 统计[L, R]的所有整数中，数字2出现的次数。\n题解 经典题，这题数据范围较小，直接暴力。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; int main() { int count = 0, l, r; cin \u0026gt;\u0026gt; l \u0026gt;\u0026gt; r; for (int i = l; i \u0026lt;= r; i++) { int x = i; while(x \u0026gt; 0) { count += (x%10 == 2); x /= 10; } } cout \u0026lt;\u0026lt; count \u0026lt;\u0026lt; endl; } 接水问题 n个学生，m个水龙头按顺序接水，计算总时间。\n方法1 模拟，时间复杂度$O(nm)$。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 10005; int n, m, a[N], b[N]; int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; for (int i = 1; i \u0026lt;= n; i++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i]); } int ans = 0; for (int i = 1; i \u0026lt;= n; i++) { int mn = 1e9, k; for (int j = 1; j \u0026lt;= m; j++) { if (mn \u0026gt; b[j]) { mn = b[j]; k = j; } } if (mn == 0) { b[k] = a[i]; } else { ans += mn; for (int j = 1; j \u0026lt;= m; j++) { b[j] -= mn; } b[k] = a[i]; } } int mx = 0; for (int j = 1; j \u0026lt;= m; j++) { mx = max(mx, b[j]); } ans += mx; cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; endl; } 方法2 也可以用优先队列，每次pop最小的出队时间，push当前同学下次出队的时间，时间复杂度到$O(n\\log{m})$。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 10005; int n, m, a[N], b[N]; int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; for (int i = 1; i \u0026lt;= n; i++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i]); } priority_queue\u0026lt;int, vector\u0026lt;int\u0026gt;, greater\u0026lt;int\u0026gt; \u0026gt; q; int cur = 0; for (int i = 1; i \u0026lt;= n; i++) { if (q.size() == m) { cur = q.top(); q.pop(); } q.push(cur + a[i]); } while(!q.empty()) { cur = q.top(); q.pop(); } cout \u0026lt;\u0026lt; cur \u0026lt;\u0026lt; endl; } 导弹拦截 给两个圆心坐标和$n$个点，求最小的半径平方和覆盖所有的点。\n题解 对每个点分别计算出到两个圆心的距离的平方，记为$a_i$，$b_i$。 最优解一定存在一个点在圆上，考虑枚举在第一个圆上的点$i$，比$a_i$小的可以被覆盖到， 比$a_i$大的需要用第二个圆覆盖，所以需要计算剩下的点中$b$的最大值。\n对$(a_i, b_i)$按照$a_i$排序，那么剩下的点就是$i+1$到$n$之间的点， 这段区间$b$的最大值可用类似前缀和的方法计算出来。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 100005; int n, b[N]; pair\u0026lt;int, int\u0026gt; a[N]; int main() { int x1, y1, x2, y2; cin \u0026gt;\u0026gt; x1 \u0026gt;\u0026gt; y1 \u0026gt;\u0026gt; x2 \u0026gt;\u0026gt; y2; cin \u0026gt;\u0026gt; n; for (int i = 1; i \u0026lt;= n; i++) { int x, y; scanf(\u0026#34;%d%d\u0026#34;, \u0026amp;x, \u0026amp;y); a[i].first = (x-x1) * (x-x1) + (y-y1)*(y-y1); a[i].second = (x-x2) * (x-x2) + (y-y2)*(y-y2); } sort(a + 1, a + n + 1); for (int i = n; i \u0026gt;= 1; i--) { b[i] = max(b[i+1], a[i].second); } int ans = 1e9; for (int i = 1; i \u0026lt;= n + 1; i++) { ans = min(ans, a[i-1].first + b[i]); } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; endl; } 三国游戏 给一个对称的$n*n$的矩阵P，n为偶数。A同学和B同学交替从$1$到$n$选数字。在所有数字选完之后，A和B分别从自己选择的$n/2$个数字中选出两个$(i,j)$，使得$P_{i,j}$最大，数字大的一方获胜。B的策略为不让A选择到当前可选的最大的组合，问A先手能否获胜，能选到的最大的$P$为多少。\n题解 在B的策略下，A和B都选不到每个数的最大匹配，所以A只能选出每个数的第二大的匹配，那么所有数的第二大匹配的最大值就是A能选出来的最大值。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 505; int a[N][N], n; int main() { cin \u0026gt;\u0026gt; n; for (int i = 1 ; i \u0026lt;= n; i++) { for (int j = i + 1; j \u0026lt;= n; j++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i][j]); a[j][i] = a[i][j]; } } int ans = 0; for (int i = 1; i \u0026lt;= n; i++) { sort(a[i]+1, a[i]+n+1); ans = max(ans, a[i][n-1]); } cout \u0026lt;\u0026lt; 1 \u0026lt;\u0026lt; endl \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; endl; } ","date":"2021-09-11T20:20:30+08:00","permalink":"https://www.961996.xyz/p/noip2010%E6%99%AE%E5%8F%8A%E7%BB%84%E9%A2%98%E8%A7%A3/","title":"NOIP2010普及组题解"},{"content":"多项式输出 给一个存了多项式系数的数组，按一定规则输出多项式。\n题解 模拟。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 105; int n, a[N]; int main() { cin \u0026gt;\u0026gt; n; for (int i = n; i \u0026gt;= 0; i--) cin \u0026gt;\u0026gt; a[i]; for (int i = n; i \u0026gt;= 0; i--) { if (a[i] == 0) { continue; } if (a[i] \u0026gt; 0) { if(i != n) { putchar(\u0026#39;+\u0026#39;); } } else { a[i] = -a[i]; putchar(\u0026#39;-\u0026#39;); } if (i \u0026gt; 1) { if (a[i] != 1) { cout \u0026lt;\u0026lt; a[i]; } cout \u0026lt;\u0026lt; \u0026#34;x^\u0026#34; \u0026lt;\u0026lt; i; } else if (i == 1) { if (a[i] != 1) { cout \u0026lt;\u0026lt; a[i]; } cout \u0026lt;\u0026lt; \u0026#34;x\u0026#34;; } else { cout \u0026lt;\u0026lt; a[i]; } } } 分数线划定 有n个选手，选出分数排行前150%*m的选手，分数相同顺延。\n题解 模拟。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 10005; int n, m; struct P { int k, s; friend int operator\u0026lt;(P a, P b) { return a.s \u0026gt; b.s || a.s == b.s \u0026amp;\u0026amp; a.k \u0026lt; b.k; } }a[N]; int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; m += m / 2; for (int i = 1; i \u0026lt;= n; i++) { cin \u0026gt;\u0026gt; a[i].k \u0026gt;\u0026gt; a[i].s; } sort(a + 1, a + n + 1); for (int i = m; i \u0026lt;= n; i++) { if (a[i].s != a[m].s) { break; } else { m = i; } } cout \u0026lt;\u0026lt; a[m].s \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; m \u0026lt;\u0026lt; endl; for (int i = 1; i \u0026lt;= m; i++) { cout \u0026lt;\u0026lt; a[i].k \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; a[i].s \u0026lt;\u0026lt; endl; } } 细胞分裂 求最小的$x$使得存在${m_1}^{m_2}|S^{x}$，如果不存在输出-1。\n题解 先将${m_1}$质因数分解${m_1}=\\prod{p_i^{n_i}}$，于是${m_1}^{m_2}=\\prod{p_i^{n_im_2}}$。 如果有解，那么$S$必然包含$m_1$分解之后所有的质因数$p_i$。假设$S$对于每个$p_i$可以分解$q_i$个，如果${m_1}^{m_2}|S^{x}$，那么$p_i^{q_ix}|p_i^{n_im_2}$，得到不等式$q_i{x}\\geq{n_im_2}$成立， 于是$x\\geq \\lceil\\frac {n_im_2} {q_i}\\rceil$， 对于S得到$x=\\min \\lceil\\frac {n_im_2} {q_i}\\rceil$\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 45000; vector\u0026lt;int\u0026gt; p; int n, m1, m2, h[N]; vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt; decomposite(int x) { vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt; res; for (int i = 0; i \u0026lt; p.size(); i++) { if (x % p[i] != 0) { continue; } int cnt = 0; while (x % p[i] == 0) { x /= p[i]; cnt += 1; } res.push_back(make_pair(p[i], cnt)); } if (x != 1) { res.push_back(make_pair(x, 1)); } return res; } int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m1 \u0026gt;\u0026gt; m2; for (int i = 2; i \u0026lt; N; i++) { if (h[i]) continue; p.push_back(i); for (int j = i + i; j \u0026lt; N; j += i) h[j] = 1; } vector\u0026lt;pair\u0026lt;int,int\u0026gt;\u0026gt; m = decomposite(m1), s; for (int i = 0; i \u0026lt; m.size(); i++) { m[i].second *= m2; } int ans = 1e9; for (int i = 1; i \u0026lt;= n; i++) { int x; cin \u0026gt;\u0026gt; x; s = decomposite(x); int mx = 0; for (int i = 0, j = 0; i \u0026lt; m.size(); i++) { while (j \u0026lt; s.size() \u0026amp;\u0026amp; m[i].first != s[j].first) j++; if (j == s.size()) { mx = 1e9; break; } mx = max(mx, (m[i].second + s[j].second - 1) / s[j].second); } ans = min(ans, mx); } if (ans \u0026gt; 1e8) { puts(\u0026#34;-1\u0026#34;); } else { cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; endl; } } 道路游戏 有n段马路组成一条环形马路，m个单位时间，每个单位时间每段马路上都会出现不同数量的金币， 第i段马路出售花费$cost[i]$金币的机器人，机器人从第i段马路开始，每走过一段马路花费一个单位时间，同时收集该段 马路在当前时间的金币，最多可以走p段马路。每个单位时间有且只有一个机器人存在，问在扣除机器人的费用之后最多可以收集多少金币。\n题解 DP，用$f[i]$表示到第$i$个时间最多能得到的金币数，转移方程为 $$f[i] = \\max_{max(0, i-p)\u0026lt;=j\u0026lt;i,1\u0026lt;=k\u0026lt;=n}(f[j]+sum[k][j+1][i]-cost[k])$$ 其中$sum[k][j+1][i]$表示从j+1时间到i时间以第k段公路为起点获得的金币总数。\n直接暴力计算$sum$，时间复杂度$O(nmp^2)$\n优化1 考虑用前缀和计算$sum[k][j+1][i]$。\n$a[i][j]$表示第i段公路，第j单位时间出现的金币。\n$s[i][j]$表示以第i段公路为起点，从第一个单位时间到第j个单位时间能获得的金币。\n$$s[i][j]=s[i][j-1]+a[(i+j-1)\\%n][j]$$ 用$t$表示第k段公路往前数j个的公路编号， 那么$s[t][i]-s[t][j]$表示从$t$开始$j$个单位时间之后能获取的金币数，而从$t$开始$j$个单位时间之后恰好是$k$， 即$s[t][i]-s[t][j]$为从j+1时间到i时间以第k段公路为起点共获得的金币。\n转移方程可以写为 $$f[i] = \\max_{\\substack{max(0, i-p)\u0026lt;=j\u0026lt;i \\\\ 0\u0026lt;=k\u0026lt;n}}(f[j]+s[t][i]-s[t][j]-cost[k])$$ 其中$t=((k-j)\\%n+n)\\%n$\n通过前缀和优化得到$O(nmp)$的算法。\n优化2 观察$f[i] = \\max(f[j]+s[t][i]-s[t][j]-cost[k])$，考虑去掉t。\n从$k$往后数$j$段公路，方程改写为 $$f[i] = \\max_{\\substack{max(0, i-p)\u0026lt;=j\u0026lt;i \\\\ 0\u0026lt;=k\u0026lt;n}}(f[j]+s[k][i]-s[k][j]-cost[(k+j)\\%n])$$ 将$k$移到外层 $$f[i] = \\max_{0\u0026lt;=k\u0026lt;n}{(s[k][i]+\\max_{max(0, i-p)\u0026lt;=j\u0026lt;i}(f[j]-s[k][j]-cost[(k+j)\\%n]))}$$ 可以发现$f[j]-s[k][j]-cost[(k+j)\\%n]$是一个跟$i$无关的表达式。\n令$$g[k][j]=f[j]-s[k][j]-cost[(k+j)\\%n]$$ 得到 $$f[i] = \\max_{0\u0026lt;=k\u0026lt;n}{(s[k][i]+\\max_{max(0, i-p)\u0026lt;=j\u0026lt;i}g[k][j])}$$ 现在要求$\\displaystyle\\max_{max(0, i-p)\u0026lt;=j\u0026lt;i}g[k][j]$，这是一个经典的RMQ问题，可以用ST表等方法优化，\n最后的时间复杂度为$O(nm\\log{m})$。\n优化3 令$j_1\u0026lt;j_2\u0026lt;i$，假设$g[k][j1] \u0026lt;= g[k][j2]$ ，那么$g[k][j1]$永远不会决策到，于是对于每个$g[k]$维护一个单调递减的队列。 时间复杂度$O(nm)$。\n参考代码 $O(nmp)$的算法就a了，并没有写更多的优化。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 1005; int n, m, p; int a[N][N], f[N], s[N][N], b[N]; int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m \u0026gt;\u0026gt; p; for (int i = 1; i \u0026lt;= n; i++) { for (int j = 1; j \u0026lt;= m; j++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i][j]); } } for (int i = 1; i \u0026lt;= n; i++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;b[i]); } for (int i = 1; i \u0026lt;= n; i++) { for (int j = 1; j \u0026lt;= m; j++) { s[i][j] = s[i][j-1] + a[(i+j-2)%n+1][j]; } } memset(f, 0xc0, sizeof(f)); f[0] = 0; for (int i = 1; i \u0026lt;= m; i++) { for (int j = max(1, i - p + 1); j \u0026lt;= i; j++) { for (int k = 1; k \u0026lt;= n; k++) { // (j, k) -\u0026gt; (1, ?)  int t = ((k-(j-1))%n+n)%n; if (t == 0) t = n; f[i] = max(f[i], f[j - 1] + s[t][i] - s[t][j - 1] - b[k]); } } } cout \u0026lt;\u0026lt; f[m] \u0026lt;\u0026lt; endl; } ","date":"2021-09-10T11:00:44+08:00","permalink":"https://www.961996.xyz/p/noip2009%E6%99%AE%E5%8F%8A%E7%BB%84%E9%A2%98%E8%A7%A3/","title":"NOIP2009普及组题解"},{"content":"ISBN号码 对于一个形式为“x-xxx-xxxxx-x”的ISBN，判断其是否合法。\n题解 模拟，需要特别处理X。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; int main() { char a[100]; scanf(\u0026#34;%s\u0026#34;, a); int n = strlen(a); int s = 0; for (int i = 0, j = 0; i \u0026lt; n - 1; i++) { if (a[i] != \u0026#39;-\u0026#39;) { j += 1; s += (a[i] - \u0026#39;0\u0026#39;) * j; } } if (s % 11 == (a[n-1] - \u0026#39;0\u0026#39;) || s % 11 == 10 \u0026amp;\u0026amp; a[n-1] == \u0026#39;X\u0026#39;) { puts(\u0026#34;Right\u0026#34;); } else { a[n-1] = s % 11 + \u0026#39;0\u0026#39;; if(s%11 == 10) { a[n-1] = \u0026#39;X\u0026#39;; } puts(a); } } 排座椅 题解 贪心，分别统计每一行每一列加一条通道能隔开的数量，优先选择最多的K条横向的和L条纵向的。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 2005; int m, n, k, l, d; pair\u0026lt;int, int\u0026gt; row[N], col[N]; int main() { cin \u0026gt;\u0026gt; m \u0026gt;\u0026gt; n \u0026gt;\u0026gt; k \u0026gt;\u0026gt; l \u0026gt;\u0026gt; d; for (int i = 1; i \u0026lt;= max(n, m); i++) { row[i].second = i; col[i].second = i; } for (int i = 1; i \u0026lt;= d; i++) { int x1, y1, x2, y2; cin \u0026gt;\u0026gt; x1 \u0026gt;\u0026gt; y1 \u0026gt;\u0026gt; x2 \u0026gt;\u0026gt; y2; if (x1 == x2) { col[min(y1, y2)].first += 1; } if (y1 == y2) { row[min(x1, x2)].first += 1; } } sort(row + 1, row + m + 1); reverse(row + 1, row + m + 1); sort(col + 1, col + n + 1); reverse(col + 1, col + n + 1); for (int i = 1; i \u0026lt;= k; i++) { swap(row[i].first, row[i].second); } sort(row + 1, row + k + 1); for (int i = 1; i \u0026lt;= k; i++) { cout \u0026lt;\u0026lt; row[i].first; if (i != k) cout \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } cout \u0026lt;\u0026lt; endl; for (int i = 1; i \u0026lt;= l; i++) { swap(col[i].first, col[i].second); } sort(col + 1, col + l + 1); for (int i = 1; i \u0026lt;= l; i++) { cout \u0026lt;\u0026lt; col[i].first; if (i != l) cout \u0026lt;\u0026lt; \u0026#39; \u0026#39;; } } 传球游戏 有n个同学排成一个圈，从1号同学开始传球，每次可以向左传或向右传，问传m次回到1号同学有多少种方式。\n题解 递推，用f[i][j]表示传了i次球，最后停留在j号同学的种数，转移方程为$$f[i][j] = f[i-1][j的左边] + f[i-1][j的右边]$$\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 35; long long f[N][N]; int n, m; int main() { f[0][1] = 1; cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; for (int i = 1; i \u0026lt;= m; i++) { for (int j = 1; j \u0026lt;= n; j++) { f[i][j] = (j == 1 ? f[i-1][n] : f[i-1][j-1]) + (j == n ? f[i-1][1] : f[i-1][j+1]); } } cout \u0026lt;\u0026lt; f[m][1] \u0026lt;\u0026lt; endl; } 立体图 题解 烂题。按从左往右，从前往后，从下往上的顺序画，之前存在的直接覆盖。\n#include \u0026lt;bits/stdc++.h\u0026gt;using namespace std; const int N = 55; int a[N][N]; int n, m; char c[2005][2005]; char b[6][8] = { \u0026#34;..+---+\u0026#34;, \u0026#34;./ /|\u0026#34;, \u0026#34;+---+ |\u0026#34;, \u0026#34;| | +\u0026#34;, \u0026#34;| |/.\u0026#34;, \u0026#34;+---+..\u0026#34;, }; int main() { cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m; int mx = 0; for (int i = 1; i \u0026lt;= n; i++) { for (int j = 1 ;j \u0026lt;= m ; j++) { scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i][j]); mx = max(a[i][j], mx); } } int px = 1e9, py = 0; int qx = 0, qy = 0; for (int i = 1; i \u0026lt;= n; i++) { for (int j = 1; j \u0026lt;= m; j++) { for (int k = 1; k \u0026lt;= a[i][j]; k++) { int x = 3 * (mx-k) + 2 * (i-1); int y = 4 * (j-1) + 2 * (n-i); px = min(px, x); for(int p = 0; p \u0026lt; 6; p++) { for(int q = 0; q \u0026lt; 7; q++) { if (b[p][q] == \u0026#39;.\u0026#39;) continue; c[x + p][y + q] = b[p][q]; qx = max(qx, x + p); qy = max(qy, y + q); } } } } } for (int i = px; i \u0026lt;= qx; i++) { for (int j = 0; j \u0026lt;= qy; j++) { if (c[i][j] == 0) cout \u0026lt;\u0026lt; \u0026#39;.\u0026#39;;else cout \u0026lt;\u0026lt; c[i][j]; } cout \u0026lt;\u0026lt; endl; } } ","date":"2021-09-05T10:42:26+08:00","permalink":"https://www.961996.xyz/p/noip2008%E6%99%AE%E5%8F%8A%E7%BB%84%E9%A2%98%E8%A7%A3/","title":"NOIP2008普及组题解"},{"content":"","date":"0001-01-01T00:00:00Z","permalink":"https://www.961996.xyz/p/","title":""}]