1. (158A) Next Round
熟悉面向对象的程序设计。
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N, K;
vector<int> A;
void Solve() {
scanf("%d%d", &N, &K);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
int answer = 0;
for (int i = 0; i < N; ++i) {
if (0 < A[i] && A[K - 1] <= A[i]) ++answer;
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
2. (158B) Taxi
常用的简化技巧:计算每个数出现了多少次。
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<int> A;
void Solve() {
scanf("%d", &N);
A.resize(5);
for (int i = 0; i < N; ++i) {
int s;
scanf("%d", &s);
++A[s];
}
int answer = A[4] + A[3] + (A[2] + 1) / 2;
A[1] -= A[3];
if (A[2] % 2 != 0) A[1] -= 2;
if (A[1] < 0) A[1] = 0;
answer += (A[1] + 3) / 4;
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
3. (455A) Boredom
先使用简化技巧,再注意到其实就是从一堆数里面选,但是不能选相邻的。记录 为从
开始能达到的最大收益,则有递推式
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<int> A, B;
void Solve() {
scanf("%d", &N);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
B.resize(1 + *max_element(A.begin(), A.end()));
for (int i = 0; i < N; ++i) ++B[A[i]];
vector<long long> f(B.size());
f[0] = 0;
for (int i = 1; i < f.size(); ++i) {
long long f1 = 2 <= i ? f[i - 2] : 0;
f[i] = max(f[i - 1], f1 + (long long)B[i] * i);
}
printf("%lld\n", f.back());
}
};
int main() {
Solution().Solve();
return 0;
}
4. (4C) Registration System
练习 map 的用法。
#include <cstdio>
#include <map>
using namespace std;
struct Solution {
int N;
map<string, int> H;
string UniqueName(const string& s) {
map<string, int>::iterator it = H.find(s);
if (it == H.end()) {
H.insert(it, make_pair(s, 0));
return "OK";
}
char buffer[80];
sprintf(buffer, "%d", ++it->second);
return s + buffer;
}
void Solve() {
scanf("%d", &N);
for (int i = 0; i < N; ++i) {
char s[33];
scanf("%s", s);
printf("%s\n", UniqueName(s).c_str());
}
}
};
int main() {
Solution().Solve();
return 0;
}
5. (466C) Number of Ways
像这种题,一般是两边分别计算。先定义
则答案为 .
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<int> A, B1, B2;
void Solve() {
scanf("%d", &N);
A.resize(N);
long long sum = 0, prefixSum = 0, suffixSum = 0;
for (int i = 0; i < N; ++i) {
scanf("%d", &A[i]);
sum += A[i];
}
if (sum % 3 != 0) {
printf("0\n");
return;
}
B1.resize(N);
for (int i = 0; i < N; ++i) {
prefixSum += A[i];
B1[i] = prefixSum == sum / 3;
}
B2.resize(N);
for (int i = N - 1; i >= 0; --i) {
suffixSum += A[i];
B2[i] = suffixSum == sum / 3;
}
for (int i = 1; i < N; ++i) B1[i] += B1[i - 1];
long long answer = 0;
for (int i = 2; i < N; ++i) {
answer += B1[i - 2] * B2[i];
}
printf("%lld\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
6. (550A) Two Substrings
熟悉 strstr() 的用法,以及字符串即字符指针的各种操作。
#include <cstdio>
#include <cstring>
using namespace std;
struct Solution {
bool Find(const char* s, const char* s1, const char* s2) {
const char *s3 = strstr(s, s1);
if (!s3) return false;
s3 += strlen(s1);
const char *s4 = strstr(s3, s2);
return s4;
}
void Solve() {
char s[100001];
scanf("%s", s);
bool answer = false;
if (Find(s, "AB", "BA")) answer = true;
if (Find(s, "BA", "AB")) answer = true;
printf(answer ? "YES\n" : "NO\n");
}
};
int main() {
Solution().Solve();
return 0;
}
7. (580C) Kefa and Park
熟悉树的表示,以及树上的动态规划方法。一般是先下传信息,再上传信息。这道题,先下传在每个节点见过几只猫,再上传有多少个叶子节点可达。
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N, M;
vector<vector<int> > Out;
vector<bool> HasCat;
void Hang(int no, int parent = -1) {
int newSize = 0;
for (int i = 0; i < Out[no].size(); ++i) {
if (Out[no][i] != parent) Out[no][newSize++] = Out[no][i];
}
Out[no].resize(newSize);
for (int i = 0; i < Out[no].size(); ++i) {
Hang(Out[no][i], no);
}
}
int Kefa(int no, int cat) {
cat = HasCat[no] ? cat + 1 : 0;
if (M < cat) return 0;
int answer = 0;
bool isLeaf = true;
for (int i = 0; i < Out[no].size(); ++i) {
answer += Kefa(Out[no][i], cat);
isLeaf = false;
}
return isLeaf ? 1 : answer;
}
void Solve() {
scanf("%d%d", &N, &M);
Out.resize(N);
HasCat.resize(N);
for (int i = 0; i < N; ++i) {
int hasCat;
scanf("%d", &hasCat);
HasCat[i] = hasCat;
}
for (int i = 0; i < N - 1; ++i) {
int v1, v2;
scanf("%d%d", &v1, &v2);
Out[v1 - 1].push_back(v2 - 1);
Out[v2 - 1].push_back(v1 - 1);
}
Hang(0);
printf("%d\n", Kefa(0, 0));
}
};
int main() {
Solution().Solve();
return 0;
}
8. (479C) Exams
考虑右端点在最左边的区间,如果能选左端点,我们就选左端点(这一定是最优的,因为没有影响别的区间),否则选右端点。这样就构成了一个贪心算法。注意,如果右端点相等的情况,我们先处理左端点靠左的线段。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct Exam {
int A, B;
bool operator<(const Exam& that) {
return A < that.A || (A == that.A && B < that.B);
}
};
struct Solution {
int N;
vector<Exam> Exams;
void Solve() {
scanf("%d", &N);
Exams.resize(N);
for (int i = 0; i < N; ++i) {
scanf("%d%d", &Exams[i].A, &Exams[i].B);
}
sort(Exams.begin(), Exams.end());
int current = 0;
for (int i = 0; i < N; ++i) {
if (Exams[i].B < current) {
current = Exams[i].A;
} else {
current = Exams[i].B;
}
}
printf("%d\n", current);
}
};
int main() {
Solution().Solve();
return 0;
}
9. (1B) Spreadsheet
数制转换题,先算出最后一位。然后减掉最后一位,除以 ,继续计算。注意学习 sscanf 和 sprintf 的用法。
#include <algorithm>
#include <cstdio>
#include <string>
using namespace std;
struct Solution {
int N;
bool Parse(const string& s, int* r, int* c) {
return sscanf(s.c_str(), "R%dC%d", r, c) == 2;
}
string FromRC(int r, int c) {
string answer;
while (c != 0) {
int c0 = (c - 1) % 26;
answer += char('A' + c0);
c = (c - (c0 + 1)) / 26;
}
reverse(answer.begin(), answer.end());
char s[80];
sprintf(s, "%d", r);
return answer += s;
}
string ToRC(string s) {
char s0[80];
int r, c = 0;
sscanf(s.c_str(), "%[A-Z]%d", s0, &r);
for (int i = 0; s0[i]; ++i) c = c * 26 + (s0[i] - 'A' + 1);
sprintf(s0, "R%dC%d", r, c);
return s0;
}
void Solve() {
scanf("%d", &N);
for (int i = 0; i < N; ++i) {
char s[80];
scanf("%s", s);
int r, c;
if (Parse(s, &r, &c)) {
printf("%s\n", FromRC(r, c).c_str());
} else {
printf("%s\n", ToRC(s).c_str());
}
}
}
};
int main() {
Solution().Solve();
return 0;
}
10. (550C) Divisibility by Eight
注意到能被 整除,则后三位能被
整除,因此答案长度不必超过
。考虑暴力搜索即可。
#include <cstdio>
#include <string>
using namespace std;
struct Solution {
string S, Result, Chosen;
bool Put(int pos) {
if (3 < Chosen.size()) return false;
if (S.size() < pos) return false;
if (Put(pos + 1)) return true;
Chosen.push_back(S[pos]);
int value;
bool success = sscanf(Chosen.c_str(), "%d", &value) == 1;
if (success && value % 8 == 0) {
Result = Chosen;
return true;
}
if (Put(pos + 1)) return true;
Chosen.pop_back();
return false;
}
void Solve() {
char s[101];
scanf("%s", s);
S = s;
if (Put(0)) {
printf("YES\n%s\n", Result.c_str());
} else {
printf("NO\n");
}
}
};
int main() {
Solution().Solve();
return 0;
}
11. (545C) Woodcutters
从左向右,每棵树先考虑向左倒,再考虑向右倒(向右倒总不比不倒差,因为向右倒至多导致后面的一棵树不能倒),贪心算法即可。
#include <cstdio>
#include <climits>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<int> X, H;
void Solve() {
scanf("%d", &N);
X.resize(N);
H.resize(N);
for (int i = 0; i < N; ++i) scanf("%d%d", &X[i], &H[i]);
int last = INT_MIN;
int answer = 0;
for (int i = 0; i < N; ++i) {
int next = i == N - 1 ? INT_MAX : X[i + 1];
if (last < X[i] - H[i]) {
++answer;
last = X[i];
} else if (X[i] + H[i] < next) {
++answer;
last = X[i] + H[i];
} else {
last = X[i];
}
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
12. (431C) k-Tree
其实是要求一个由 组成的串,其中至少有一个值
, 和为
,问放法数量。令
为和为
要求有值
的放法数量,
为和为
但不要求有值
的放法数量。
令
能算出 和
的递推关系,且不含求和号如下:
#include <cstdio>
#include <vector>
using namespace std;
const int M = 1000000007;
struct Solution {
int N, K, D;
vector<long long> F, G;
void Solve() {
scanf("%d%d%d", &N, &K, &D);
F.resize(N + 1);
G.resize(N + 1);
F[0] = 0;
G[0] = 1;
for (int i = 1; i <= N; ++i) {
long long g1 = i - K >= 1 ? G[i - K - 1] : 0;
long long g2 = i >= D ? G[i - D] : 0;
long long f2 = i >= D ? F[i - D] : 0L;
long long gi = (G[i - 1] + (G[i - 1] - g1)) % M;
G[i] = (gi + M) % M;
long long fi = (F[i - 1] + (F[i - 1] - f2) + (g2 - g1)) % M;
F[i] = (fi + M) % M;
}
long long answer = (F[N] - F[N - 1]) % M;
printf("%lld\n", (answer + M) % M);
}
};
int main() {
Solution().Solve();
return 0;
}
13. (474D) Flowers
动态规划。令 表示长度为
的花有多少种吃法。则
. 为了算出
的区间和,不妨求出
的前缀和,然后两个前缀和相减就是部分和。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int M = 1000000007;
struct Solution {
int T, K;
vector<int> A, B;
void Solve() {
scanf("%d%d", &T, &K);
A.resize(T);
B.resize(T);
for (int i = 0; i < T; ++i) {
scanf("%d%d", &A[i], &B[i]);
}
int m = *max_element(B.begin(), B.end());
vector<int> f(m + 1);
f[0] = 1;
for (int i = 1; i <= m; ++i) {
int f1 = K <= i ? f[i - K] : 0;
f[i] = (f[i - 1] + f1) % M;
}
for (int i = 1; i <= m; ++i) f[i] = (f[i] + f[i - 1]) % M;
for (int i = 0; i < T; ++i) {
printf("%d\n", (f[B[i]] + M - f[A[i] - 1]) % M);
}
}
};
int main() {
Solution().Solve();
return 0;
}
14. (580B) Kefa and Company
先按照钱的多少排序,然后邀请的朋友们一定是连续的一段。计算每个点结尾的友谊因子之和的最大值即可,令 表示这个最大值从何处开始,我们可以证明
是单调增的。因此得到了一个
的算法。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
bool AssignMax(long long* p, long long v) {
if (*p < v) return *p = v, true;
return false;
}
struct Person {
int M, S;
bool operator<(const Person& that) { return M < that.M; }
};
struct Solution {
int N, D;
vector<Person> A;
void Solve() {
scanf("%d%d", &N, &D);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d%d", &A[i].M, &A[i].S);
sort(A.begin(), A.end());
int p1 = 0, p2 = 0;
long long current = 0, answer = 0;
while (p2 < A.size()) {
while (A[p1].M + D <= A[p2].M) current -= A[p1++].S;
AssignMax(&answer, current += A[p2++].S);
}
printf("%lld\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
15. (2A) Winner
简单的模拟题,学习一下如何遍历一个 map。注意合并特殊情况至一般情况,这是信息学常见的简化程序实现的技巧。
#include <cstdio>
#include <map>
#include <string>
#include <vector>
using namespace std;
bool AssignMax(int* p, int v) {
if (*p < v) return *p = v, true;
return false;
}
struct Solution {
int N;
vector<string> Name;
vector<int> Score;
void Solve() {
scanf("%d", &N);
Name.resize(N);
Score.resize(N);
map<string, int> finalScore;
for (int i = 0; i < N; ++i) {
char s[33];
scanf("%s%d", s, &Score[i]);
Name[i] = s;
finalScore[Name[i]] += Score[i];
}
int maxScore = 0;
for (
map<string, int>::iterator it = finalScore.begin();
it != finalScore.end();
++it
) AssignMax(&maxScore, it->second);
map<string, int> currentScore;
for (int i = 0; i < N; ++i) {
int score = currentScore[Name[i]] += Score[i];
if (maxScore <= score && finalScore[Name[i]] == maxScore) {
printf("%s\n", Name[i].c_str());
return;
}
}
}
};
int main() {
Solution().Solve();
return 0;
}
16. (377A) Maze
可以想到一个贪心算法,一开始先把度为 的点删去,再把新的度为
的点删去……可是有环的情况怎么办呢?考虑一个生成树就可以了。不妨对原图做一个 DFS,然后沿着节点序的逆序删除,每次删除的点一定是叶子节点,这样就做完了。
#include <cstdio>
#include <stack>
#include <string>
#include <vector>
using namespace std;
char S[501];
struct Solution {
int N, M, K;
vector<string> A;
vector<pair<int, int> > DFSOrder, Connect4;
Solution() {
Connect4.push_back(make_pair(1, 0));
Connect4.push_back(make_pair(0, 1));
Connect4.push_back(make_pair(-1, 0));
Connect4.push_back(make_pair(0, -1));
}
void AnyFreeCell(int* x, int* y) {
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
if (A[i][j] == '.') {
*x = i;
*y = j;
return;
}
}
}
}
void DFS(int x, int y) {
stack<pair<int, int> > stack0;
stack0.push(make_pair(x, y));
A[x][y] = 'V';
while (!stack0.empty()) {
int x = stack0.top().first;
int y = stack0.top().second;
stack0.pop();
DFSOrder.push_back(make_pair(x, y));
for (int i = 0; i < 4; ++i) {
int dx = Connect4[i].first;
int dy = Connect4[i].second;
int newX = x + dx;
int newY = y + dy;
if (newX < 0 || N <= newX) continue;
if (newY < 0 || M <= newY) continue;
if (A[newX][newY] == '.') {
A[newX][newY] = 'V';
stack0.push(make_pair(newX, newY));
}
}
}
}
void Solve() {
scanf("%d%d%d", &N, &M, &K);
A.resize(N);
for (int i = 0; i < N; ++i) {
scanf("%s", S);
A[i] = S;
}
int x, y;
AnyFreeCell(&x, &y);
DFS(x, y);
for (int i = 0; i < K; ++i) {
x = DFSOrder[DFSOrder.size() - 1 - i].first;
y = DFSOrder[DFSOrder.size() - 1 - i].second;
A[x][y] = 'X';
}
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
if (A[i][j] == 'V') A[i][j] = '.';
}
printf("%s\n", A[i].c_str());
}
}
};
int main() {
Solution().Solve();
return 0;
}
17. (414B) Mashmokh and ACM
容易想到动态规划,令 表示从
开始,长度为
有多少种。则
直接计算的话需要枚举约数,不如直接拿出上一个阶段的每个目标函数,看看能更新哪些目标函数,结论是能更新所有 的倍数,这样就方便了。还可以使用滚动数组进一步优化,注意到每个值只会更新
更大的值,因此从大到小更新可以直接在原数组上更改,更方便。
#include <cstdio>
#include <vector>
using namespace std;
const int M = 1000000007;
struct Solution {
int N, K;
vector<int> F;
void Solve() {
scanf("%d%d", &N, &K);
F.resize(N + 1);
for (int i = 0; i <= N; ++i) F[i] = 1;
for (int i = 1; i < K; ++i) {
for (int j = N; j >= 1; --j) {
for (int k = j + j; k <= N; k += j) {
F[k] = (F[k] + F[j]) % M;
}
}
}
int answer = 0;
for (int i = 1; i <= N; ++i) answer = (answer + F[i]) % M;
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
18. (4B) Before an Exam
先满足每天的最小值,剩下的贪心地分配即可。注意输出不要多额外的空格。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int D, SumTime;
vector<int> MinTime, MaxTime;
void Solve() {
scanf("%d%d", &D, &SumTime);
MinTime.resize(D);
MaxTime.resize(D);
int sumMinTime = 0, sumMaxTime = 0;
for (int i = 0; i < D; ++i) {
scanf("%d%d", &MinTime[i], &MaxTime[i]);
sumMinTime += MinTime[i];
sumMaxTime += MaxTime[i];
}
if (SumTime < sumMinTime || sumMaxTime < SumTime) {
printf("NO\n");
return;
}
printf("YES\n");
bool head = true;
SumTime -= sumMinTime;
for (int i = 0; i < D; ++i) {
int flexibleTime = MaxTime[i] - MinTime[i];
int actualTime = min(SumTime, flexibleTime);
SumTime -= actualTime;
if (!head) putchar(' ');
head = false;
printf("%d", actualTime + MinTime[i]);
}
printf("\n");
}
};
int main() {
Solution().Solve();
return 0;
}
19. (339D) Xenia and Bit Operations
最简单的一个线段树。注意这个线段树的每层的转移是不一样的,和当前层数的奇偶性有关。
#include <cstdio>
#include <vector>
using namespace std;
struct SegTree {
int K, M;
vector<int> Tree;
SegTree(int k) {
K = k;
M = 1 << K;
Tree.resize(M << 1);
}
void Compute(int p, int k) {
if ((K - k) & 1) {
Tree[p] = Tree[p + p] | Tree[p + p + 1];
} else {
Tree[p] = Tree[p + p] ^ Tree[p + p + 1];
}
}
void Build() {
for (int i = K - 1; i >= 0; --i) {
for (int j = 1 << i; j < (1 << (i + 1)); ++j) Compute(j, i);
}
}
int Set(int p, int b) {
p += M;
Tree[p] = b;
for (int i = 1; i <= K; ++i) Compute(p >> i, K - i);
return Tree[1];
}
};
struct Solution {
int N, M;
void Solve() {
scanf("%d%d", &N, &M);
SegTree tree(N);
for (int i = 0; i < (1 << N); ++i) scanf("%d", &tree.Tree[tree.M + i]);
tree.Build();
for (int i = 0; i < M; ++i) {
int p, b;
scanf("%d%d", &p, &b);
printf("%d\n", tree.Set(p - 1, b));
}
}
};
int main() {
Solution().Solve();
return 0;
}
20. (166E) Tetrahedron
令 为走
步返回原点的方案数,
为走
步没有返回原点的方案数。则有
整理得 ,就是动态规划了。如果时限卡更紧,可以数学解通项或者矩阵快速幂计算。
#include <cstdio>
using namespace std;
const int M = 1000000007;
struct Solution {
int N;
void Solve() {
scanf("%d", &N);
long long a0 = 1, a1 = 0;
for (int i = 2; i <= N; ++i) {
long long a2 = (2 * a1 + 3 * a0) % M;
a0 = a1;
a1 = a2;
}
printf("%lld\n", a1);
}
};
int main() {
Solution().Solve();
return 0;
}
21. (277A) Learning Languages
把每个人, 每种语言都当做点,这就是一个二分图,求连通分量个数。需要注意的是,如果所有人都什么语言都不会,是个特殊情况,需要特殊判断。
#include <cstdio>
#include <stack>
#include <vector>
using namespace std;
struct Solution {
int N, M;
vector<vector<int> > Out;
vector<bool> Visited;
void DFS(int no) {
stack<int> stack0;
stack0.push(no);
Visited[no] = true;
while (!stack0.empty()) {
int n = stack0.top();
stack0.pop();
for (int i = 0; i < Out[n].size(); ++i) {
int target = Out[n][i];
if (Visited[target]) continue;
Visited[target] = true;
stack0.push(target);
}
}
}
bool NoneKnown() {
for (int i = 0; i < N; ++i) {
if (0 < Out[i].size()) return false;
}
return true;
}
void Solve() {
scanf("%d%d", &N, &M);
Out.resize(N + M);
for (int i = 0; i < N; ++i) {
int k;
scanf("%d", &k);
for (int j = 0; j < k; ++j) {
int language;
scanf("%d", &language);
Out[i].push_back(N + language - 1);
Out[N + language - 1].push_back(i);
}
}
if (NoneKnown()) {
printf("%d\n", N);
} else {
Visited.resize(Out.size());
int answer = 0;
for (int i = 0; i < N; ++i) {
if (Visited[i]) continue;
++answer;
DFS(i);
}
printf("%d\n", answer - 1);
}
}
};
int main() {
Solution().Solve();
return 0;
}
22. (478C) Table Decorations
如果 ,多出的部分是不能用的。反之一定能找到方案,使得总气球除以
取整就是答案。证明可能比较复杂,此处不再展开,但结论是直观的。
#include <cstdio>
using namespace std;
bool AssignMin(long long* p, long long v) {
if (v < *p) return *p = v, true;
return false;
}
struct Solution {
long long R, G, B;
void Solve() {
scanf("%lld%lld%lld", &R, &G, &B);
AssignMin(&R, G + G + B + B);
AssignMin(&G, B + B + R + R);
AssignMin(&B, R + R + G + G);
printf("%lld\n", (R + G + B) / 3);
}
};
int main() {
Solution().Solve();
return 0;
}
23. (650A) Watchmen
欧氏距离等于曼哈顿距离,则两个点的连线与坐标轴平行。注意相同的两个点可能会被计算两次。这里使用模板来简化程序。
#include <cstdio>
#include <map>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<pair<int, int>> A;
template <class T, class Get>
long long Compute(Get get) {
map<T, int> map0;
for (int i = 0; i < A.size(); ++i) ++map0[get(A[i])];
long long answer = 0;
for (
typename map<T, int>::iterator it = map0.begin();
it != map0.end();
++it
) answer += (long long)it->second * (it->second - 1) / 2;
return answer;
}
static int GetFirst(const pair<int, int>& p) { return p.first; }
static int GetSecond(const pair<int, int>& p) { return p.second; }
static const pair<int, int>& GetWhole(const pair<int, int>& p) { return p; }
void Solve() {
scanf("%d", &N);
A.resize(N);
for (int i = 0; i < N; ++i) {
int x, y;
scanf("%d%d", &x, &y);
A[i] = make_pair(x, y);
}
long long A1 = Compute<int>(GetFirst);
long long A2 = Compute<int>(GetSecond);
long long A3 = Compute<pair<int, int> >(GetWhole);
printf("%lld\n", A1 + A2 - A3);
}
};
int main() {
Solution().Solve();
return 0;
}
24. (276C) Little Girl and Maximum Sum
重点计算每个数组中的数被累加了多少次,然后使用排序不等式即可。这里不需要用树状数组或线段树,只要计算原数组差分以后的值就行。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N, Q;
vector<int> A, B;
void Solve() {
scanf("%d%d", &N, &Q);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
B.resize(N + 2);
for (int i = 0; i < Q; ++i) {
int left, right;
scanf("%d%d", &left, &right);
++B[left];
--B[right + 1];
}
for (int i = 0; i <= N; ++i) B[i + 1] += B[i];
sort(A.begin(), A.end());
sort(B.begin(), B.end());
long long answer = 0;
for (int i = 0; i < N; ++i) {
answer += (long long)A[i] * B[i + 2];
}
printf("%lld\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
25. (550B) Preparing Olympiad
递归穷举搜索,记录当前的最大值,最小值,和,已经有多少数。这个题 比较小,而数据范围比较大,因此无法使用动态规划。
#include <algorithm>
#include <climits>
#include <cstdio>
#include <vector>
using namespace std;
bool AssignMin(int* p, int v) {
if (v < *p) return *p = v, true;
return false;
}
bool AssignMax(int* p, int v) {
if (*p < v) return *p = v, true;
return false;
}
struct Solution {
int N, L, R, X, Answer;
vector<int> C;
void Put(int no, int num, int sum, int minimum, int maximum) {
if (num + N - no < 2) return;
if (no == N) {
if (X <= maximum - minimum && L <= sum && sum <= R) ++Answer;
return;
}
Put(no + 1, num, sum, minimum, maximum);
AssignMin(&minimum, C[no]);
AssignMax(&maximum, C[no]);
Put(no + 1, num + 1, sum + C[no], minimum, maximum);
}
void Solve() {
scanf("%d%d%d%d", &N, &L, &R, &X);
C.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &C[i]);
Answer = 0;
Put(0, 0, 0, INT_MAX, INT_MIN);
printf("%d\n", Answer);
}
};
int main() {
Solution().Solve();
return 0;
}
26. (687A) NP-Hard Problem
其实就是判断原图是不是一个二分图,使用 DFS 判断即可。
#include <cstdio>
#include <stack>
#include <vector>
using namespace std;
struct Solution {
int N, M;
vector<vector<int> > Out;
vector<int> A[2], Visited;
bool DFS(int no, int part) {
stack<int> stack0;
Visited[no] = part;
stack0.push(no);
while (!stack0.empty()) {
no = stack0.top();
part = Visited[no];
A[part - 1].push_back(no);
stack0.pop();
for (int i = 0; i < Out[no].size(); ++i) {
int target = Out[no][i];
if (Visited[target] == 0) {
Visited[target] = 3 - part;
stack0.push(target);
} else if (Visited[target] != 3 - part) {
return false;
}
}
}
return true;
}
bool Bipartite() {
Visited.resize(N);
for (int i = 0; i < N; ++i) {
if (!Visited[i] && !DFS(i, 1)) return false;
}
return true;
}
void Print(const vector<int>& a) {
printf("%d\n", a.size());
bool head = true;
for (int i = 0; i < a.size(); ++i) {
if (!head) putchar(' ');
head = false;
printf("%d", a[i] + 1);
}
printf("\n");
}
void Solve() {
scanf("%d%d", &N, &M);
Out.resize(N);
for (int i = 0; i < M; ++i) {
int u, v;
scanf("%d%d", &u, &v);
Out[u - 1].push_back(v - 1);
Out[v - 1].push_back(u - 1);
}
if (Bipartite()) {
Print(A[0]);
Print(A[1]);
} else {
printf("-1\n");
}
}
};
int main() {
Solution().Solve();
return 0;
}
27. (706C) Hard problem
令 为第
个串,
为第
个串翻转。
为前
个串有序所得到的最小权值,其中第
个串在
时不翻转,在
时翻转。状态转移如下,
#include <algorithm>
#include <cstdio>
#include <climits>
#include <string>
#include <vector>
using namespace std;
char Buffer[100001];
bool AssignMin(long long* p, long long v) {
if (v < *p) return *p = v, true;
return false;
}
struct Solution {
int N;
vector<long long> C, F[2];
vector<string> S, R;
void Solve() {
scanf("%d", &N);
C.resize(N);
for (int i = 0; i < N; ++i) scanf("%lld", &C[i]);
S.resize(N);
R.resize(N);
for (int i = 0; i < N; ++i) {
scanf("%s", Buffer);
S[i] = Buffer;
R[i] = Buffer;
reverse(R[i].begin(), R[i].end());
}
for (int i = 0; i < 2; ++i) F[i].resize(N);
F[0][0] = 0;
F[1][0] = C[0];
for (int i = 1; i < N; ++i) {
F[0][i] = F[1][i] = LONG_LONG_MAX >> 1;
if (S[i - 1] <= S[i]) AssignMin(&F[0][i], F[0][i - 1]);
if (S[i - 1] <= R[i]) AssignMin(&F[1][i], F[0][i - 1] + C[i]);
if (R[i - 1] <= S[i]) AssignMin(&F[0][i], F[1][i - 1]);
if (R[i - 1] <= R[i]) AssignMin(&F[1][i], F[1][i - 1] + C[i]);
}
long long answer = min(F[0][N - 1], F[1][N - 1]);
printf("%lld\n", answer == LONG_LONG_MAX >> 1 ? -1 : answer);
}
};
int main() {
Solution().Solve();
return 0;
}
28. (576A) Vasya and Petya's Game
只需判断所有素数的方幂即可断定一个数。反之可以用抽屉原理证明,比较困难,此处不展开。
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<int> AllPrimes, Answer;
vector<bool> Composite;
void Generate() {
Composite.resize(N + 1);
for (int i = 2; i <= N; ++i) {
if (!Composite[i]) {
AllPrimes.push_back(i);
for (long long j = (long long)i * i; j <= N; j += i) {
Composite[j] = true;
}
}
}
}
void Solve() {
scanf("%d", &N);
Generate();
for (int i = 0; i < AllPrimes.size(); ++i) {
for (int j = AllPrimes[i]; j <= N; j *= AllPrimes[i]) {
Answer.push_back(j);
}
}
printf("%d\n", Answer.size());
bool head = true;
for (int i = 0; i < Answer.size(); ++i) {
if (!head) putchar(' ');
head = false;
printf("%d", Answer[i]);
}
printf("\n");
}
};
int main() {
Solution().Solve();
return 0;
}
29. (676C) Vasya and String
计算每个点结尾的最大长度即可,令 表示这个最大长度从何处开始,我们可以证明
是单调增的。因此得到了一个
的算法。
#include <algorithm>
#include <cstdio>
#include <string>
using namespace std;
char Buffer[100001];
bool AssignMax(int* p, int v) {
if (*p < v) return *p = v, true;
return false;
}
struct Solution {
int N, K;
string S;
int Maximize(char a) {
int p = 0, q = 0, b = 0, answer = 0;
while (q < N) {
if (S[q++] != a) ++b;
while (K < b) {
if (S[p++] != a) --b;
}
AssignMax(&answer, q - p);
}
return answer;
}
void Solve() {
scanf("%d%d", &N, &K);
scanf("%s", Buffer);
S = Buffer;
printf("%d\n", max(Maximize('a'), Maximize('b')));
}
};
int main() {
Solution().Solve();
return 0;
}
30. (559A) Gerald's Hexagon
计算多边形面积,用向量的叉积就可以了。用向量法可以得到每个点的坐标。
#include <cstdio>
#include <complex>
#include <math.h>
using namespace std;
const double PI = acos(0.0) * 2.0;
typedef complex<double> Point;
double Cross(Point a1, Point a2) {
return a1.real() * a2.imag() - a1.imag() * a2.real();
}
struct Solution {
int A[6];
Point P[6];
double Area() {
double answer = 0.0;
for (int i = 0; i < 6; ++i) {
Point a1 = P[i];
Point a2 = i == 5 ? P[0] : P[i + 1];
answer += Cross(a1, a2);
}
return answer;
}
void Solve() {
scanf("%d%d%d%d%d%d", &A[0], &A[1], &A[2], &A[3], &A[4], &A[5]);
for (int i = 0; i < 6; ++i) {
P[i] = polar((double)A[i], PI / 3.0 * i);
}
Point last;
for (int i = 0; i < 6; ++i) {
last = P[i] += last;
}
printf("%.0lf\n", Area() / (sin(PI / 3.0)));
}
};
int main() {
Solution().Solve();
return 0;
}
31. (437C) The Child and Toy
每条边恰被删去一次,能否保证删去时所付出的权值是小的那边呢?很简单,只要从大到小删就可以了。需要按照权值大小对序号进行排序,写自定义比较器即可。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct Compare {
const vector<int>* V;
Compare(const vector<int>& v) { V = &v; }
bool operator()(int i1, int i2) {
return V->at(i2) < V->at(i1);
}
};
struct Solution {
int N, M;
vector<int> V, No;
vector<vector<int> > Out;
vector<bool> Removed;
void Solve() {
scanf("%d%d", &N, &M);
V.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &V[i]);
Out.resize(N);
for (int i = 0; i < M; ++i) {
int u, v;
scanf("%d%d", &u, &v);
Out[u - 1].push_back(v - 1);
Out[v - 1].push_back(u - 1);
}
No.resize(N);
for (int i = 0; i < N; ++i) No[i] = i;
sort(No.begin(), No.end(), Compare(V));
Removed.resize(N);
int answer = 0;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < Out[No[i]].size(); ++j) {
int target = Out[No[i]][j];
if (Removed[target]) continue;
answer += V[target];
}
Removed[No[i]] = true;
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
32. (762A) k-th divisor
可以用 时间算出
的所有约数,再从其中找出第
大的数。注意,对
是完全平方数的情况,不要重复计数
。
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
vector<int64> Divisors(int64 n) {
vector<int64> answer1, answer2;
int64 i = 1;
for (; i * i < n; ++i) {
if (n % i == 0) {
answer1.push_back(i);
answer2.push_back(n / i);
}
}
if (i * i == n) answer1.push_back(i);
for (int i = answer2.size() - 1; i >= 0; --i) {
answer1.push_back(answer2[i]);
}
return answer1;
}
struct Solution {
int64 N;
int K;
void Solve() {
scanf("%lld%d", &N, &K);
vector<int64> divisors = Divisors(N);
printf("%lld\n", K <= divisors.size() ? divisors[K - 1] : -1);
}
};
int main() {
Solution().Solve();
return 0;
}
33. (268C) Beautiful Sets of Points
容易想到用一条 的直线上的点,但是从
出发是不可以的,因此从长方形另外的顶点出发。
#include <algorithm>
#include <cstdio>
using namespace std;
struct Solution {
int N, M;
void Solve() {
scanf("%d%d", &N, &M);
int k = min(N, M);
printf("%d\n", k + 1);
for (int i = 0; i <= k; ++i) printf("%d %d\n", N - i, i);
}
};
int main() {
Solution().Solve();
return 0;
}
34. (446A) DZY Loves Sequences
动态规划。令 为以第
个数结尾最长上升列的长度,
为以第
个数结尾最长上升列,允许一次修改(不能修改首尾的数)的长度。则
则答案就是
#include <cstdio>
#include <vector>
using namespace std;
bool AssignMax(int* p, int v) {
if (*p < v) return *p = v, true;
return false;
}
bool AssignMin(int* p, int v) {
if (v < *p) return *p = v, true;
return false;
}
struct Solution {
int N;
vector<int> A;
void Solve() {
scanf("%d", &N);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
int f0 = 1, f1 = A[1] > A[0] ? 2 : 1;
int g0 = 1, g1 = 2;
int answer = 2;
for (int i = 2; i < N; ++i) {
int f2 = 1, g2 = 1;
if (A[i - 1] < A[i]) f2 = f1 + 1;
if (A[i - 1] < A[i]) g2 = g1 + 1;
if (A[i - 2] + 1 < A[i]) AssignMax(&g2, f0 + 2);
AssignMax(&answer, f2 + 1);
AssignMax(&answer, g2);
f0 = f1, f1 = f2;
g0 = g1, g1 = g2;
}
AssignMin(&answer, N);
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
35. (510B) Fox And Two Dots
使用一次 DFS 即可,如果有指向已经访问过的点的边,就是有环。
#include <cstdio>
#include <stack>
#include <string>
#include <vector>
using namespace std;
char Buffer[51];
int DeltaX[] = {1, 0, -1, 0};
int DeltaY[] = {0, 1, 0, -1};
struct Node {
int X, Y, lastX, lastY;
};
struct Solution {
int N, M;
vector<string> A;
bool Cycle;
void DFS(int x, int y) {
char c = A[x][y];
A[x][y] = '.';
stack<Node> stack0;
Node node = {x, y, -1, -1};
stack0.push(node);
while (!stack0.empty()) {
Node node = stack0.top();
stack0.pop();
A[node.X][node.Y] = '.';
for (int i = 0; i < 4; ++i) {
int newX = node.X + DeltaX[i];
int newY = node.Y + DeltaY[i];
if (newX < 0 || N <= newX || newY < 0 || M <= newY) continue;
if (newX == node.lastX && newY == node.lastY) continue;
if (A[newX][newY] == '.') Cycle = true;
if (A[newX][newY] == c) {
A[newX][newY] = '.';
Node newNode = {newX, newY, node.X, node.Y};
stack0.push(newNode);
}
}
A[node.X][node.Y] = '#';
}
}
void Solve() {
scanf("%d%d", &N, &M);
A.resize(N);
for (int i = 0; i < N; ++i) {
scanf("%s", Buffer);
A[i] = Buffer;
}
Cycle = false;
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
if (A[i][j] != '#') DFS(i, j);
}
}
printf("%s\n", Cycle ? "Yes" : "No");
}
};
int main() {
Solution().Solve();
return 0;
}
36. (522A) Reposts
字典,累加。用不着建图:输入就是按照转发顺序来的,因此直接依次计算就好了。
#include <cctype>
#include <cstdio>
#include <cstring>
#include <map>
#include <string>
using namespace std;
char Buffer1[25], Buffer2[25];
bool AssignMax(int *p, int v) {
if (*p < v) return *p = v, true;
return false;
}
struct Solution {
map<string, int> Length;
int N;
string Lower(const char* s) {
string answer;
answer.reserve(strlen(s));
for (int i = 0; s[i]; ++i) answer += char(tolower(s[i]));
return answer;
}
void Solve() {
Length["polycarp"] = 1;
scanf("%d", &N);
int answer = 0;
for (int i = 0; i < N; ++i) {
scanf("%s reposted %s", Buffer1, Buffer2);
string s1 = Lower(Buffer1);
string s2 = Lower(Buffer2);
int length = Length[s1] = Length[s2] + 1;
AssignMax(&answer, length);
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
37. (707C) Pythagorean Triples
若输入不是 的方幂,那么去掉质因数
的数作为一条直角边即可。如果输入是
的方幂,只要
,其平方可以分解为
和另一个偶数的乘积,分别作为
和
即可。只有
和
不能构成勾股三角形。
#include <cstdio>
using namespace std;
typedef long long int64;
struct Solution {
int N;
void Solve() {
scanf("%d", &N);
if (N == 1 || N == 2) {
printf("-1\n");
} else {
int lowbit = N & -N;
N /= lowbit;
if (N == 1) {
int64 sum = int64(lowbit) * lowbit / 2;
int64 difference = 2;
printf("%lld %lld\n",
(sum + difference) / 2, (sum - difference) / 2);
} else {
int64 sum = int64(N) * N;
int64 difference = 1;
printf("%lld %lld\n",
(sum + difference) / 2 * lowbit,
(sum - difference) / 2 * lowbit);
}
}
}
};
int main() {
Solution().Solve();
return 0;
}
38. (888A) Local Extrema
直接暴力求解。
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<int> A;
void Solve() {
scanf("%d", &N);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
int answer = 0;
for (int i = 1; i < N - 1; ++i) {
if (A[i - 1] < A[i] && A[i + 1] < A[i]) ++answer;
if (A[i] < A[i - 1] && A[i] < A[i + 1]) ++answer;
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
39. (118D) Caesar's Legions
令 为方案数。则
#include <cstdio>
#include <vector>
using namespace std;
const int M = 100000000;
struct Solution {
int N1, N2, K1, K2;
vector<vector<int> > F1, F2;
void Solve() {
scanf("%d%d%d%d", &N1, &N2, &K1, &K2);
F1.resize(N1 + 1);
F2.resize(N1 + 1);
for (int i = 0; i <= N1; ++i) {
F1[i].resize(N2 + 1);
F2[i].resize(N2 + 1);
if (i == 0) F1[i][0] = F2[i][0] = 1;
for (int j = 0; j <= N2; ++j) {
for (int k = 1; k <= K1; ++k) {
if (i - k < 0) continue;
F1[i][j] = (F1[i][j] + F2[i - k][j]) % M;
}
for (int k = 1; k <= K2; ++k) {
if (j - k < 0) continue;
F2[i][j] = (F2[i][j] + F1[i][j - k]) % M;
}
}
}
printf("%d\n", (F1[N1][N2] + F2[N1][N2]) % M);
}
};
int main() {
Solution().Solve();
return 0;
}
40. (27A) Next Test
用 set 存储数,然后暴力枚举即可。
#include <cstdio>
#include <set>
using namespace std;
struct Solution {
int N;
set<int> A;
void Solve() {
scanf("%d", &N);
for (int i = 0; i < N; ++i) {
int v;
scanf("%d", &v);
A.insert(v);
}
int answer = 1;
while (A.count(answer)) ++answer;
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
41. (467C) George and Job
令 为到
结束,取
段最大的和,则答案是
。有状态转移
这样得到一个 的算法。重新想一个状态转移
这样得到一个 的算法。
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
bool AssignMax(int64* p, int64 v) {
if (*p < v) return *p = v, true;
return false;
}
struct Solution {
int N, M, K;
vector<int64> A;
vector<vector<int64> > F;
void Solve() {
scanf("%d%d%d", &N, &M, &K);
A.resize(N + 1);
for (int i = 1; i <= N; ++i) scanf("%lld", &A[i]);
for (int i = 1; i <= N; ++i) A[i] += A[i - 1];
F.resize(N + 1);
F[0].resize(K + 1);
for (int i = 1; i <= N; ++i) {
F[i].resize(K + 1);
for (int j = 1; j <= K; ++j) {
AssignMax(&F[i][j], F[i - 1][j]);
if (M <= i) {
AssignMax(&F[i][j], A[i] - A[i - M] + F[i - M][j - 1]);
}
}
}
printf("%lld\n", F[N][K]);
}
};
int main() {
Solution().Solve();
return 0;
}
42. (584B) Kolya and Tanya
总数减不满意的方案数即可:。快速幂取模。
#include <cstdio>
using namespace std;
typedef long long int64;
const int M = 1000000007;
int PowerMod(int a, int b, int m) {
int answer = 1;
while (b != 0) {
if ((b & 1) != 0) answer = int64(answer) * a % m;
a = int64(a) * a % m;
b >>= 1;
}
return answer;
}
struct Solution {
int N;
void Solve() {
scanf("%d", &N);
int a1 = PowerMod(27, N, M);
int a2 = PowerMod(7, N, M);
printf("%d\n", (a1 + M - a2) % M);
}
};
int main() {
Solution().Solve();
return 0;
}
43. (235A) LCM Challenge
如果数较小,可以手工计算。如果数较大时,对于奇数 即可,对于偶数,如果是
的倍数,
,如果不是,
。
#include <cstdio>
using namespace std;
typedef long long int64;
struct Solution {
int N;
void Solve() {
scanf("%d", &N);
if (N == 1) {
printf("1\n");
} else if (N == 2) {
printf("2\n");
} else if (N == 3) {
printf("6\n");
} else if (N == 4) {
printf("12\n");
} else if (N % 2 != 0) {
printf("%lld\n", int64(N) * (N - 1) * (N - 2));
} else if (N % 3 != 0) {
printf("%lld\n", int64(N) * (N - 1) * (N - 3));
} else {
printf("%lld\n", int64(N - 1) * (N - 2) * (N - 3));
}
}
};
int main() {
Solution().Solve();
return 0;
}
44. (510C) Fox And Names
题目给出一些要求,比如某字母必须排在另一个字母的前面等等,要求给出所有字母的排列。这是一个拓扑排序问题,可以用 DFS 完成。
#include <algorithm>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
char Buffer[101];
struct Solution {
int N;
string Last, Current, Answer;
vector<vector<bool> > Less;
vector<int> Visited;
bool Failure;
bool AddEdge() {
for (int i = 0; ; ++i) {
if (i == Last.size()) return true;
if (i == Current.size()) return false;
if (Last[i] != Current[i]) {
Less[Last[i] - 'a'][Current[i] - 'a'] = true;
return true;
}
}
}
bool DFS(int no) {
Visited[no] = 1;
for (int i = 0; i < 26; ++i) {
if (!Less[no][i]) continue;
if (Visited[i] == 1) return false;
if (Visited[i] == 0 && !DFS(i)) return false;
}
Visited[no] = 2;
Answer += char('a' + no);
return true;
}
bool TopoSort() {
Visited.resize(26);
for (int i = 0; i < 26; ++i) {
if (Visited[i] == 0 && !DFS(i)) return false;
}
reverse(Answer.begin(), Answer.end());
return Answer.size() == 26;
}
void Solve() {
Less.resize(26);
for (int i = 0; i < 26; ++i) Less[i].resize(26);
scanf("%d", &N);
Failure = false;
for (int i = 0; i < N; ++i) {
scanf("%s", Buffer);
Current = Buffer;
if (!AddEdge()) Failure = true;
Last = Current;
}
if (Failure || !TopoSort()) {
printf("Impossible\n");
} else {
printf("%s\n", Answer.c_str());
}
}
};
int main() {
Solution().Solve();
return 0;
}
45. (839C) Journey
树上 DP,计算每个点出发向叶子方向路径长度的期望即可。
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<vector<int> > G;
double ExpectLength(int no, int parent) {
double total = 0.0;
int leaves = 0;
for (int i = 0; i < G[no].size(); ++i) {
if (G[no][i] == parent) continue;
++leaves;
total += ExpectLength(G[no][i], no) + 1.0;
}
if (leaves == 0) return 0;
return total / leaves;
}
void Solve() {
scanf("%d", &N);
G.resize(N);
for (int i = 1; i < N; ++i) {
int v1, v2;
scanf("%d%d", &v1, &v2);
G[v1 - 1].push_back(v2 - 1);
G[v2 - 1].push_back(v1 - 1);
}
printf("%.12f\n", ExpectLength(0, -1));
}
};
int main() {
Solution().Solve();
return 0;
}
46. (735D) Taxes
从分析样例开始。 只收
卢布的税,一定是拆成了
个质数。想到任何奇数都能拆成
个质数之和,而偶数都能拆成
个质数之和(Goldbach 猜想),答案必定不超过
。剩下就是讨论了。
#include <cstdio>
using namespace std;
bool Prime(int n) {
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) return false;
}
return true;
}
struct Solution {
int N;
void Solve() {
scanf("%d", &N);
if (Prime(N)) {
printf("1\n");
} else if (N % 2 == 0 || Prime(N - 2)) {
printf("2\n");
} else {
printf("3\n");
}
}
};
int main() {
Solution().Solve();
return 0;
}
47. (570C) Replacement
其实就是问有几个“..”。直接在原序列上维护,每次修改序列之前,把曾经有的“..”从答案中删除掉,然后修改之后把新的添上去。
#include <cstdio>
#include <string>
using namespace std;
char Buffer[300001];
struct Solution {
int N, M;
string S;
void Solve() {
scanf("%d%d", &N, &M);
scanf("%s", Buffer);
S = Buffer;
int answer = 0;
for (int i = 0; i < N - 1; ++i) {
if (S[i] == '.' && S[i + 1] == '.') ++answer;
}
for (int i = 0; i < M; ++i) {
int place;
char newChar;
scanf("%d %c", &place, &newChar);
--place;
if (place + 1 < N && S[place] == '.' && S[place + 1] == '.') {
--answer;
}
if (0 <= place - 1 && S[place - 1] == '.' && S[place] == '.') {
--answer;
}
S[place] = newChar;
if (place + 1 < N && S[place] == '.' && S[place + 1] == '.') {
++answer;
}
if (0 <= place - 1 && S[place - 1] == '.' && S[place] == '.') {
++answer;
}
printf("%d\n", answer);
}
}
};
int main() {
Solution().Solve();
return 0;
}
48. (460B) Little Dima and Equation
这题目 范围虽然大,但
范围不大。枚举
的值即可。
#include <cstdio>
#include <vector>
using namespace std;
const int M = 1000000000;
struct Solution {
int A, B, C;
int DigitSum(int x) {
int answer = 0;
while (x != 0) {
answer += x % 10;
x /= 10;
}
return answer;
}
void Solve() {
scanf("%d%d%d", &A, &B, &C);
vector<int> answer;
for (int i = 1; i <= 81; ++i) {
long long x = B;
for (int j = 0; j < A; ++j) x *= i;
x += C;
if (x < M && DigitSum(x) == i) answer.push_back(x);
}
printf("%d\n", answer.size());
if (!answer.empty()) {
bool head = true;
for (int i = 0; i < answer.size(); ++i) {
if (!head) putchar(' ');
head = false;
printf("%d", answer[i]);
}
printf("\n");
}
}
};
int main() {
Solution().Solve();
return 0;
}
49. (771A) Bear and Friendship Condition
其实就是要求图是若干个不联通的团。判断是否是团,只需计数总边数为 就可以了。
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
struct Solution {
int N, M;
vector<vector<int> > Out;
vector<bool> Visited;
void DFS(int no, int* nodes, int* edges) {
Visited[no] = true;
++*nodes;
for (int i = 0; i < Out[no].size(); ++i) {
++*edges;
int target = Out[no][i];
if (Visited[target]) continue;
DFS(target, nodes, edges);
}
}
bool Reasonable() {
Visited.resize(N + 1);
for (int i = 1; i < N; ++i) {
if (Visited[i]) continue;
int nodes = 0, edges = 0;
DFS(i, &nodes, &edges);
if (int64(nodes) * (nodes - 1) != edges) return false;
}
return true;
}
void Solve() {
scanf("%d%d", &N, &M);
Out.resize(N + 1);
for (int i = 0; i < M; ++i) {
int v1, v2;
scanf("%d%d", &v1, &v2);
Out[v1].push_back(v2);
Out[v2].push_back(v1);
}
printf(Reasonable() ? "YES\n" : "NO\n");
}
};
int main() {
Solution().Solve();
return 0;
}
50. (349B) Color the Fence
贪心法,首先选择费用最小的使得总长度最大,然后在保证总长度最大的前提下,选字典序最大的解。
#include <algorithm>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
struct Solution {
int V, MinA, Length;
vector<int> A;
string ComputeAnswer() {
string answer;
int exceed = V - Length * MinA;
for (int i = 9; ; --i) {
while (A[i] - MinA <= exceed) {
exceed -= A[i] - MinA;
answer += char('0' + i);
if (answer.size() == Length) return answer;
}
}
}
void Solve() {
scanf("%d", &V);
A.resize(10);
for (int i = 1; i < 10; ++i) scanf("%d", &A[i]);
MinA = *min_element(A.begin() + 1, A.end());
Length = V / MinA;
if (Length == 0) {
printf("-1\n");
} else {
printf("%s\n", ComputeAnswer().c_str());
}
}
};
int main() {
Solution().Solve();
return 0;
}