51. (126B) Password
在串上跑 KMP 算法,记得到的数组为 。
首先若 ,说明无解。
若存在 ,说明
就是解。
再看 ,如果为零,说明无解。原因是如果它为零,找不到更短的字符串使得同时为前缀后缀。
如果不为零,这个串出现了至少四次,在开头的开头,开头的结尾,结尾的开头,结尾的结尾:就是答案。
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
char Buffer[1000001];
struct Solution {
string S;
vector<int> Next;
int GetAnswer() {
if (Next[S.size()] == 0) return 0;
for (int i = 0; i < S.size(); ++i) {
if (Next[i] == Next[S.size()]) return Next[S.size()];
}
if (Next[Next[S.size()]] == 0) return 0;
return Next[Next[S.size()]];
}
void Solve() {
scanf("%s", Buffer);
S = Buffer;
Next.resize(S.size() + 1);
int k = Next[0] = -1;
for (int i = 0; i < S.size(); ++i) {
while (k >= 0 && S[k] != S[i]) k = Next[k];
Next[i + 1] = ++k;
}
int answer = GetAnswer();
if (answer == 0) {
printf("Just a legend\n");
} else {
printf("%s\n", S.substr(0, answer).c_str());
}
}
};
int main() {
Solution().Solve();
return 0;
}
52. (701C) They Are Everywhere
用两个指针的经典方法,维护不同元素的个数即可。
#include <cstdio>
#include <climits>
#include <string>
#include <vector>
using namespace std;
bool AssignMin(int *p, int v) {
if (v < *p) return *p = v, true;
return false;
}
char Buffer[100001];
struct Bag {
vector<int> A;
int NonZero;
Bag() {
A.resize(256);
NonZero = 0;
}
void Add(char c) {
if (A[c]++ == 0) ++NonZero;
}
void Remove(char c) {
if (--A[c] == 0) --NonZero;
}
};
struct Solution {
int N;
string S;
void Solve() {
scanf("%d %s", &N, Buffer);
S = Buffer;
Bag bag, bag0;
for (int i = 0; i < S.size(); ++i) bag.Add(S[i]);
int different = bag.NonZero;
int p = 0, q = 0, answer = INT_MAX;
while (true) {
if (bag0.NonZero < different) {
if (q == S.size()) break;
bag0.Add(S[q++]);
} else {
AssignMin(&answer, q - p);
bag0.Remove(S[p++]);
}
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
53. (493B) Vasya and Wrestling
模拟题,注意简化程序。
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
typedef long long int64;
struct Solution {
int N, Last;
vector<int> B1, B2;
int64 Sum(const vector<int>& v) {
int64 answer = 0;
for (int i = 0; i < v.size(); ++i) {
answer += v[i];
}
return answer;
}
string Winner() {
int64 sum1 = Sum(B1);
int64 sum2 = Sum(B2);
if (sum2 < sum1) return "first";
if (sum1 < sum2) return "second";
for (int i = 0; ; ++i) {
if (B1.size() <= i && B2.size() <= i) break;
if (B1.size() <= i) return "second";
if (B2.size() <= i) return "first";
if (B1[i] < B2[i]) return "second";
if (B2[i] < B1[i]) return "first";
}
if (Last == 1) return "first";
if (Last == 2) return "second";
return string();
}
void Solve() {
scanf("%d", &N);
for (int i = 0; i < N; ++i) {
int a;
scanf("%d", &a);
if (0 < a) {
B1.push_back(a);
Last = 1;
} else {
B2.push_back(-a);
Last = 2;
}
}
printf("%s\n", Winner().c_str());
}
};
int main() {
Solution().Solve();
return 0;
}
54. (348A) Mafia
首先总参与量必须合适,其次每个人的参与次数必须小于等于总比赛次数。至于为什么这样就能保证一定有方案,需要反过来想,因为当裁判的人是任意的,所以相当于给定每个人当裁判的总次数,然后要求方案。依次当够总次数即可。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
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]);
int64 sum = 0;
for (int i = 0; i < N; ++i) sum += A[i];
int64 answer = (sum + N - 2) / (N - 1);
int64 maxA = *max_element(A.begin(), A.end());
printf("%lld\n", max(maxA, answer));
}
};
int main() {
Solution().Solve();
return 0;
}
55. (742B) Arpa’s obvious problem and Mehrdad’s terrible solution
用 map 统计每个数出现的次数即可。注意:用 multiset 会超时。
#include <cstdio>
#include <map>
#include <vector>
using namespace std;
typedef long long int64;
struct Solution {
int N, X;
vector<int> A;
map<int, int> S;
void Solve() {
scanf("%d%d", &N, &X);
A.resize(N);
for (int i = 0; i < N; ++i) {
scanf("%d", &A[i]);
++S[A[i]];
}
int64 answer = 0;
if (X == 0) {
for (int i = 0; i < N; ++i) {
answer += S[A[i]] - 1;
}
} else {
for (int i = 0; i < N; ++i) {
answer += S[A[i] ^ X];
}
}
printf("%lld\n", answer / 2);
}
};
int main() {
Solution().Solve();
return 0;
}
56. (518A) Vitaly and Strings
取字典序恰比第一个串大的串,然后和第二个串比较。
#include <cstdio>
#include <string>
using namespace std;
char Buffer[101];
struct Solution {
string S1, S2, S3;
string Inc(string s) {
int k = s.size() - 1;
while (k >= 0) {
if (s[k] != 'z') {
++s[k];
break;
}
--k;
}
for (int i = k + 1; i < s.size(); ++i) s[i] = 'a';
return s;
}
void Solve() {
scanf("%s", Buffer);
S1 = Buffer;
scanf("%s", Buffer);
S2 = Buffer;
S3 = Inc(S1);
if (S3 < S2) {
printf("%s\n", S3.c_str());
} else {
printf("%s\n", "No such string");
}
}
};
int main() {
Solution().Solve();
return 0;
}
57. (601A) The Two Routes
首先考虑从 到
的路,如果是汽车,那么汽车可以直接到达终点,反之火车可以直接到达终点。所以只要 BFS 算出另一种车到终点的时间就可以了。
#include <algorithm>
#include <cstdio>
#include <climits>
#include <queue>
#include <vector>
using namespace std;
struct Solution {
int N, M;
vector<vector<bool> > Railway;
int BFS(int start, int end, bool rail) {
vector<int> distance(N);
for (int i = 0; i < N; ++i) distance[i] = INT_MAX;
distance[start] = 0;
queue<int> q;
q.push(start);
while (!q.empty()) {
int front = q.front();
q.pop();
for (int i = 0; i < N; ++i) {
if (Railway[front][i] != rail) continue;
if (distance[i] != INT_MAX) continue;
distance[i] = distance[front] + 1;
q.push(i);
}
}
return distance[end];
}
void Solve() {
scanf("%d%d", &N, &M);
Railway.resize(N);
for (int i = 0; i < N; ++i) Railway[i].resize(N);
for (int i = 0; i < M; ++i) {
int v1, v2;
scanf("%d%d", &v1, &v2);
Railway[v1 - 1][v2 - 1] = true;
Railway[v2 - 1][v1 - 1] = true;
}
int answer1 = BFS(0, N - 1, false);
int answer2 = BFS(0, N - 1, true);
int answer = max(answer1, answer2);
printf("%d\n", answer == INT_MAX ? -1 : answer);
}
};
int main() {
Solution().Solve();
return 0;
}
58. (743C) Vladik and fractions
写个搜索找规律得到 三个数满足条件。
#include <cstdio>
using namespace std;
struct Solution {
int N;
void Solve() {
scanf("%d", &N);
if (N == 1) {
printf("-1\n");
} else {
printf("%d %d %d\n", N, N + 1, N * (N + 1));
}
}
};
int main() {
Solution().Solve();
return 0;
}
59. (713A) Sonya and Queries
结果只和每个数每位的奇偶有关,因此可以把每个数当作二进制数存。直接一个大数组存下来即可。
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
const int M = 18;
char Buffer[19];
struct Solution {
int T;
vector<int> A;
int ToBinary(const string& b) {
int answer = 0;
for (int i = 0; i < b.size(); ++i) {
answer = answer + answer + b[i] % 2;
}
return answer;
}
void Solve() {
A.resize(1 << M);
scanf("%d", &T);
for (int i = 0; i < T; ++i) {
char op;
scanf(" %c %s", &op, Buffer);
int x = ToBinary(Buffer);
switch (op) {
case '+': ++A[x]; break;
case '-': --A[x]; break;
case '?': printf("%d\n", A[x]); break;
}
}
}
};
int main() {
Solution().Solve();
return 0;
}
60. (371C) Hamburgers
先手工做汉堡,把现有的材料用完,然后就相当于是纯买材料制作汉堡了。
#include <algorithm>
#include <cstdio>
using namespace std;
char Buffer[101];
typedef long long int64;
struct Solution {
int NeedB, NeedS, NeedC;
int HaveB, HaveS, HaveC;
int PriceB, PriceS, PriceC;
int64 Money;
void Solve() {
scanf("%s", Buffer);
NeedB = NeedS = NeedC = 0;
for (int i = 0; Buffer[i]; ++i) {
switch (Buffer[i]) {
case 'B': ++NeedB; break;
case 'S': ++NeedS; break;
case 'C': ++NeedC; break;
}
}
scanf("%d%d%d", &HaveB, &HaveS, &HaveC);
scanf("%d%d%d", &PriceB, &PriceS, &PriceC);
scanf("%lld", &Money);
int64 answer = 0;
while (HaveB && NeedB || HaveS && NeedS || HaveC && NeedC) {
int64 needMoneyB = max(NeedB - HaveB, 0) * PriceB;
int64 needMoneyS = max(NeedS - HaveS, 0) * PriceS;
int64 needMoneyC = max(NeedC - HaveC, 0) * PriceC;
int64 needMoney = needMoneyB + needMoneyS + needMoneyC;
if (Money < needMoney) break;
++answer;
Money -= needMoney;
HaveB = HaveB + needMoneyB / PriceB - NeedB;
HaveS = HaveS + needMoneyS / PriceS - NeedS;
HaveC = HaveC + needMoneyC / PriceC - NeedC;
}
int64 eachMoneyB = NeedB * PriceB;
int64 eachMoneyS = NeedS * PriceS;
int64 eachMoneyC = NeedC * PriceC;
int64 eachMoney = eachMoneyB + eachMoneyS + eachMoneyC;
printf("%lld\n", answer + Money / eachMoney);
}
};
int main() {
Solution().Solve();
return 0;
}
61. (611C) New Year and Domino
每个点 能放横向骨牌,等价于
和
都是空。统计每一个前缀矩形能放横向骨牌的个数,然后就可以计算出任意询问。纵向同理。
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
char Buffer[501];
struct Solution {
int H, W, Q;
vector<string> Grid, GridT;
vector<vector<int> > Prefix, PrefixT;
vector<vector<int> > ComputePrefix(const vector<string>& grid) {
vector<vector<int> > prefix(grid.size() + 1);
for (int i = 0; i < prefix.size(); ++i) {
prefix[i].resize(grid[0].size() + 1);
}
for (int i = 0; i < grid.size(); ++i) {
for (int j = 0; j < grid[i].size() - 1; ++j) {
prefix[i + 1][j + 1] =
grid[i][j] == '.' && grid[i][j + 1] == '.';
}
}
for (int i = 0; i < prefix.size(); ++i) {
for (int j = 1; j < prefix[i].size(); ++j) {
prefix[i][j] += prefix[i][j - 1];
}
}
for (int i = 1; i < prefix.size(); ++i) {
for (int j = 0; j < prefix[i].size(); ++j) {
prefix[i][j] += prefix[i - 1][j];
}
}
return prefix;
}
int Compute(const vector<vector<int> >& prefix,
int r1, int c1, int r2, int c2) {
int a1 = prefix[r2][c2];
int a2 = prefix[r2][c1 - 1];
int a3 = prefix[r1 - 1][c2];
int a4 = prefix[r1 - 1][c1 - 1];
return a1 - a2 - a3 + a4;
}
void Solve() {
scanf("%d%d", &H, &W);
Grid.resize(H);
for (int i = 0; i < H; ++i) {
scanf("%s", Buffer);
Grid[i] = Buffer;
}
GridT.resize(W);
for (int i = 0; i < W; ++i) {
GridT[i].resize(H);
for (int j = 0; j < H; ++j) {
GridT[i][j] = Grid[j][i];
}
}
Prefix = ComputePrefix(Grid);
PrefixT = ComputePrefix(GridT);
scanf("%d", &Q);
for (int i = 0; i < Q; ++i) {
int r1, c1, r2, c2;
scanf("%d%d%d%d", &r1, &c1, &r2, &c2);
int answer1 = Compute(Prefix, r1, c1, r2, c2 - 1);
int answer2 = Compute(PrefixT, c1, r1, c2, r2 - 1);
printf("%d\n", answer1 + answer2);
}
}
};
int main() {
Solution().Solve();
return 0;
}
62. (441C) Valera and Tubes
蛇形输出所有格子,任意切分成若干条管道即可。
#include <cstdio>
using namespace std;
struct Snake {
int N, M, X, Y, DY;
Snake(int n, int m) {
N = n, M = m;
X = 1, Y = 1, DY = 1;
}
void Next() {
Y += DY;
if (Y == 0) {
++X;
Y = 1;
DY = -DY;
} else if (Y == M + 1) {
++X;
Y = M;
DY = -DY;
}
}
};
struct Solution {
int N, M, K;
void Solve() {
scanf("%d%d%d", &N, &M, &K);
Snake snake(N, M);
for (int i = 0; i < K - 1; ++i) {
printf("2 %d %d ", snake.X, snake.Y);
snake.Next();
printf("%d %d\n", snake.X, snake.Y);
snake.Next();
}
int total = N * M - (K + K - 2);
printf("%d", total);
for (int i = 0; i < total; ++i) {
printf(" %d %d", snake.X, snake.Y);
snake.Next();
}
printf("\n");
}
};
int main() {
Solution().Solve();
}
63. (559B) Equivalent Strings
对每个字符串,计算一个哈希值:如果长度为奇数,就是要求有序的哈希值;如果长度为偶数,就是前后两段可以无序的哈希值。
#include <cstdio>
#include <string>
using namespace std;
typedef unsigned long long uint64;
uint64 Fingerprint(uint64 x) {
const uint64 kMul = 0x9ddfea08eb382d69ULL;
x *= kMul, x ^= x >> 47;
x *= kMul, x ^= x >> 47;
x *= kMul, x ^= x >> 47;
return x * kMul;
}
char Buffer[200001];
struct Solution {
string S1, S2;
uint64 Hash(int size, const char* s) {
if (size % 2 == 0) {
uint64 finger1 = Fingerprint(Hash(size / 2, s));
uint64 finger2 = Fingerprint(Hash(size / 2, s + size / 2));
return finger1 + finger2;
}
uint64 answer = 0;
for (int i = 0; i < size; ++i) {
answer = Fingerprint(answer + s[i]);
}
return answer;
}
void Solve() {
scanf("%s", Buffer);
S1 = Buffer;
scanf("%s", Buffer);
S2 = Buffer;
if (Hash(S1.size(), S1.c_str()) == Hash(S2.size(), S2.c_str())) {
printf("YES\n");
} else {
printf("NO\n");
}
}
};
int main() {
Solution().Solve();
return 0;
}
64. (486C) Palindrome Transformation
如果 在后一半,我们对称到前一半。然后,有两种方法:
- 先把
移到
,再移到
。
- 先把
移到
,再移到
。
取其中答案较小的即可。
#include <algorithm>
#include <cstdio>
#include <string>
using namespace std;
char Buffer[100001];
int Difference(char c1, char c2) {
int d1 = (c1 - c2 + 26) % 26;
int d2 = (c2 - c1 + 26) % 26;
return min(d1, d2);
}
struct ToPalindrome {
string S;
int N, P, Answer;
void GoLeft() {
int target = 0;
while (target <= P && S[target] == S[N - 1 - target]) ++target;
while (true) {
Answer += Difference(S[P], S[N - 1 - P]);
S[P] = S[N - 1 - P];
if (P <= target) break;
++Answer;
--P;
}
}
void GoRight() {
int target = N / 2 - 1;
while (P <= target && S[target] == S[N - 1 - target]) --target;
while (true) {
Answer += Difference(S[P], S[N - 1 - P]);
S[P] = S[N - 1 - P];
if (target <= P) break;
++Answer;
++P;
}
}
int GetAnswer1() {
GoRight();
GoLeft();
return Answer;
}
int GetAnswer2() {
GoLeft();
GoRight();
return Answer;
}
};
struct Solution {
int N, P;
string S;
void Solve() {
scanf("%d%d%s", &N, &P, Buffer), --P;
S = Buffer;
if (N / 2 < P) {
P = N - 1 - P;
reverse(S.begin(), S.end());
}
ToPalindrome to1 = {S, N, P, 0};
ToPalindrome to2 = {S, N, P, 0};
printf("%d\n", min(to1.GetAnswer1(), to2.GetAnswer2()));
}
};
int main() {
Solution().Solve();
return 0;
}
65. (295A) Greg and Array
把原数组差分,这样可以常数时间修改一个区间。需要统计每个操作进行的次数,同样,采用差分的方法来统计。把每个操作的 扩大次数倍,然后直接修改就可以了。
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
struct Operation {
int L, R;
int64 D;
};
struct Query {
int X, Y;
};
struct Solution {
int N, M, K;
vector<int64> A;
vector<Operation> Ops;
vector<int> OpsTime;
vector<Query> Queries;
void Solve() {
scanf("%d%d%d", &N, &M, &K);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
Ops.resize(M);
for (int i = 0; i < M; ++i) {
scanf("%d%d%lld", &Ops[i].L, &Ops[i].R, &Ops[i].D);
--Ops[i].L, --Ops[i].R;
}
Queries.resize(K);
for (int i = 0; i < K; ++i) {
scanf("%d%d", &Queries[i].X, &Queries[i].Y);
--Queries[i].X, --Queries[i].Y;
}
OpsTime.resize(M + 1);
for (int i = 0; i < K; ++i) {
++OpsTime[Queries[i].X];
--OpsTime[Queries[i].Y + 1];
}
for (int i = N - 1; i > 0; --i) A[i] -= A[i - 1];
A.push_back(0);
for (int i = 0; i < M; ++i) {
OpsTime[i + 1] += OpsTime[i];
Ops[i].D *= OpsTime[i];
A[Ops[i].L] += Ops[i].D;
A[Ops[i].R + 1] -= Ops[i].D;
}
for (int i = 1; i < N; ++i) A[i] += A[i - 1];
bool head = true;
for (int i = 0; i < N; ++i) {
if (!head) putchar(' ');
head = false;
printf("%lld", A[i]);
}
printf("\n");
}
};
int main() {
Solution().Solve();
return 0;
}
66. (1114B) Yet Another Array Partitioning Task
首先答案一定是最大的 个数。然后把最大的
个数设置成
,其余数设置成
,从左向右扫描一遍就可以得到所有的分界点。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
struct LessBy {
const vector<int>* A;
LessBy(const vector<int>& a) {A = &a;}
bool operator()(int a1, int a2) {
return A->at(a1) < A->at(a2);
}
};
struct Solution {
int N, M, K;
vector<int> A;
void Solve() {
scanf("%d%d%d", &N, &M, &K);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
vector<int> b(N);
for (int i = 0; i < N; ++i) b[i] = i;
sort(b.begin(), b.end(), LessBy(A));
int64 answer = 0;
for (int i = N - M * K; i < N; ++i) answer += A[b[i]];
printf("%lld\n", answer);
for (int i = 0; i < N; ++i) A[i] = 0;
for (int i = N - M * K; i < N; ++i) A[b[i]] = 1;
bool head = true;
int p = 0;
for (int i = 1; i < K; ++i) {
for (int j = 0; j < M; ++j) {
while (A[p] == 0) ++p;
++p;
}
if (!head) putchar(' ');
head = false;
printf("%d", p);
}
printf("\n");
}
};
int main() {
Solution().Solve();
}
67. (500B) New Year Permutation
如果两个位置可交换,那么这两个位置就是等价的。在每个等价类里,都可以对原排列排序。如何根据等价关系得到等价类呢?其实就是求连通分量就可以了。注意程序中对数组的一部分进行排序的写法。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<int> A;
vector<vector<char> > Adjacent;
vector<bool> Visited;
vector<int> CurrentComponent;
void DFS(int no) {
Visited[no] = true;
CurrentComponent.push_back(no);
for (int i = 0; i < N; ++i) {
if (Adjacent[no][i] == '1' && !Visited[i]) DFS(i);
}
}
void SortCurrentComponent() {
sort(CurrentComponent.begin(), CurrentComponent.end());
vector<int> b(CurrentComponent.size());
for (int i = 0; i < b.size(); ++i) b[i] = A[CurrentComponent[i]];
sort(b.begin(), b.end());
for (int i = 0; i < b.size(); ++i) A[CurrentComponent[i]] = b[i];
}
void Solve() {
scanf("%d", &N);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
Adjacent.resize(N);
for (int i = 0; i < N; ++i) {
Adjacent[i].resize(N);
for (int j = 0; j < N; ++j) {
scanf(" %c", &Adjacent[i][j]);
}
}
Visited.resize(N);
for (int i = 0; i < N; ++i) {
if (!Visited[i]) {
DFS(i);
SortCurrentComponent();
CurrentComponent.clear();
}
}
bool head = true;
for (int i = 0; i < N; ++i) {
if (!head) putchar(' ');
head = false;
printf("%d", A[i]);
}
printf("\n");
}
};
int main() {
Solution().Solve();
return 0;
}
68. (1190A) Tokitsukaze and Discard Items
模拟。从前往后扫描一遍就可以算出每个点删除的时候在第几页,然后能顺便带走同一页的点。
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
struct Solution {
int64 N, M, K;
vector<int64> P;
void Solve() {
scanf("%lld%lld%lld", &N, &M, &K);
P.resize(M);
for (int i = 0; i < M; ++i) scanf("%lld", &P[i]);
int answer = 0;
for (int i = 0; i < M; ) {
int64 page = (P[i] - i - 1) / K;
int j = i + 1;
for (; j < M; ++j) {
int64 page1 = (P[j] - i - 1) / K;
if (page1 != page) break;
}
++answer;
i = j;
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
69. (1106D) Lunar New Year and a Wander
有点像最小生成树的 Prim 算法:贪心选择所有与当前连通部分相邻的,还没加入连通部分的,标号最小的点加入连通部分。
#include <cstdio>
#include <set>
#include <vector>
using namespace std;
struct Solution {
int N, M;
vector<vector<int> > Out;
vector<bool> Visited;
set<int> Q;
void Solve() {
scanf("%d%d", &N, &M);
Out.resize(N);
for (int i = 0; i < M; ++i) {
int v1, v2;
scanf("%d%d", &v1, &v2);
Out[v1 - 1].push_back(v2 - 1);
Out[v2 - 1].push_back(v1 - 1);
}
Visited.resize(N);
Visited[0] = true;
Q.insert(0);
bool head = true;
while (!Q.empty()) {
int front = *Q.begin();
Q.erase(Q.begin());
if (!head) putchar(' ');
head = false;
printf("%d", front + 1);
for (int i = 0; i < Out[front].size(); ++i) {
int target = Out[front][i];
if (!Visited[target]) {
Visited[target] = true;
Q.insert(target);
}
}
}
printf("\n");
}
};
int main() {
Solution().Solve();
return 0;
}
70. (525B) Pasha and String
求出翻转次数的前缀和,就是特定的一位是否被翻转过。
#include <algorithm>
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
char Buffer[200001];
struct Solution {
string S;
vector<bool> Reversed;
int M, A;
void Solve() {
scanf("%s%d", Buffer, &M);
S = Buffer;
Reversed.resize(S.size() / 2);
for (int i = 0; i < M; ++i) {
scanf("%d", &A);
Reversed[A - 1] = Reversed[A - 1] ^ 1;
}
for (int i = 1; i < Reversed.size(); ++i) {
Reversed[i] = Reversed[i] ^ Reversed[i - 1];
}
for (int i1 = 0; i1 < S.size(); ++i1) {
int i2 = S.size() - 1 - i1;
int iMin = min(i1, i2);
if (iMin < Reversed.size() && Reversed[iMin]) {
putchar(S[i2]);
} else {
putchar(S[i1]);
}
}
printf("\n");
}
};
int main() {
Solution().Solve();
return 0;
}
71. (484A) Bits
先把询问变成开区间,然后如果左端点二进制最后一位是 0,处理它的兄弟;如果右端点二进制最后一位是 1,处理它的兄弟。然后左右儿子各往上一层。很像是 zkw 线段树的处理方法。
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long int64;
bool AssignMin(int64* p, int64 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;
int64 L, R;
int PopCount(int64 x) {
int answer = 0;
while (x != 0) {
x &= x - 1;
++answer;
}
return answer;
}
int64 MaxPopCount(int64 boundL, int64 boundR) {
int depth = 0, answer = 0;
int64 arg = 0;
int popCountL = PopCount(boundL);
int popCountR = PopCount(boundR);
while ((boundL ^ boundR) != 1) {
if ((boundL & 1) == 0) {
int answer1 = depth + popCountL + 1;
int64 arg1 = ((boundL + 1 + 1) << depth) - 1;
if (AssignMax(&answer, answer1)) {
arg = arg1;
} else if (answer == answer1) {
AssignMin(&arg, arg1);
}
}
if ((boundR & 1) != 0) {
int answer1 = depth + popCountR - 1;
int64 arg1 = ((boundR - 1 + 1) << depth) - 1;
if (AssignMax(&answer, answer1)) {
arg = arg1;
} else if (answer == answer1) {
AssignMin(&arg, arg1);
}
}
if (boundL & 1) --popCountL;
boundL >>= 1;
if (boundR & 1) --popCountR;
boundR >>= 1;
++depth;
}
return arg;
}
void Solve() {
scanf("%d", &N);
for (int i = 0; i < N; ++i) {
scanf("%lld%lld", &L, &R);
printf("%lld\n", MaxPopCount(max(0LL, L - 1), R + 1));
}
}
};
int main() {
Solution().Solve();
return 0;
}
72. (1201B) Zero Array
如果总和是奇数,不可能。如果最大的数比剩余的数加起来还大,不可能。否则可以把最大的数和第二大的数减一,保持前两个性质不变,最终得到零。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
struct Solution {
int N;
vector<int> A;
bool CanDecrease() {
scanf("%d", &N);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
int64 sum = 0, max = *max_element(A.begin(), A.end());
for (int i = 0; i < N; ++i) sum += A[i];
if (sum % 2 != 0) return false;
if (max + max > sum) return false;
return true;
}
void Solve() {
printf(CanDecrease() ? "YES\n" : "NO\n");
}
};
int main() {
Solution().Solve();
return 0;
}
73. (812C) Sagheer and Nubian Market
买 件商品可行,那么买
件商品也可行。因此可以二分答案,查找最小的不可行的
,然后减一就是最大可行的
,再算需要花的钱。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
struct Solution {
int N, S;
vector<int> A;
int64 FindSum(int k) {
vector<int64> price(N);
for (int i = 0; i < N; ++i) price[i] = A[i] + k * int64(i + 1);
sort(price.begin(), price.end());
int64 sum = 0;
for (int i = 0; i < k; ++i) sum += price[i];
return sum;
}
int BinarySearch(int lower, int upper) {
while (lower < upper) {
int mid = lower + (upper - lower) / 2;
if (FindSum(mid) <= S) {
lower = mid + 1;
} else {
upper = mid;
}
}
return lower;
}
void Solve() {
scanf("%d%d", &N, &S);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
int items = BinarySearch(1, N + 1) - 1;
int64 sum = FindSum(items);
printf("%d %lld\n", items, sum);
}
};
int main() {
Solution().Solve();
return 0;
}
74. (165C) Another Problem on Strings
把 1 看作分隔符,把原数组整理成最开始的 1 前面,每两个 1 之间,和最后的 1 后面有多少个 0 的形式。然后答案是显而易见的。注意, 是特殊情况。
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
typedef long long int64;
char Buffer[1000001];
struct Solution {
int N;
string S;
vector<int> A;
void Solve() {
scanf("%d%s", &N, Buffer);
S = Buffer;
A.push_back(0);
for (int i = 0; i < S.size(); ++i) {
if (S[i] == '0') {
++A.back();
} else {
A.push_back(0);
}
}
int64 answer = 0;
if (N == 0) {
for (int i = 0; i < A.size(); ++i) {
answer += int64(A[i] + 1) * A[i] / 2;
}
} else {
for (int i = 0; i + N < A.size(); ++i) {
answer += int64(A[i] + 1) * (A[i + N] + 1);
}
}
printf("%lld\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
75. (778A) String Game
如果一个串不包含指定子串,删除若干字符后依然不会包含。因此可以二分答案,计算最小的操作次数,使得操作之后不包含指定子串,然后减一就是答案。
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
char Buffer[200001];
struct Solution {
string S, S1;
vector<int> P;
bool OK(int k) {
vector<bool> Removed(S.size());
for (int i = 0; i < k; ++i) Removed[P[i]] = true;
int p = 0;
for (int i = 0; i < S.size(); ++i) {
if (Removed[i]) continue;
if (S[i] == S1[p]) ++p;
if (p == S1.size()) return true;
}
return false;
}
int BinarySearch(int lower, int upper) {
while (lower < upper) {
int mid = lower + (upper - lower) / 2;
if (OK(mid)) {
lower = mid + 1;
} else {
upper = mid;
}
}
return lower;
}
void Solve() {
scanf("%s", Buffer);
S = Buffer;
scanf("%s", Buffer);
S1 = Buffer;
P.resize(S.size());
for (int i = 0; i < P.size(); ++i) {
scanf("%d", &P[i]);
--P[i];
}
int maxOK = BinarySearch(0, P.size() + 1) - 1;
printf("%d\n", maxOK);
}
};
int main() {
Solution().Solve();
return 0;
}
76. (9C) Hexadecimal's Numbers
本题比较简单,可以枚举,但是作为一般的方法,可以考虑类似 zkw 线段树的方法,先变成开区间,然后判断最后一位,之后去掉最后一位再讨论。如果这题目改成九进制数,枚举就不好用了。
#include <cstdio>
using namespace std;
struct Solution {
int N;
int InvalidNum(int n) {
int answer = 0;
while (n != 0) {
answer += (1 < n % 10);
n /= 10;
}
return answer;
}
void Solve() {
scanf("%d", &N);
++N;
int invalidNum = InvalidNum(N);
int depth = 0, answer = 0;
while (N != 0) {
invalidNum -= (1 < N % 10);
if (invalidNum == 0 && 0 < N % 10) answer += 1 << depth;
if (invalidNum == 0 && 1 < N % 10) answer += 1 << depth;
N /= 10;
++depth;
}
printf("%d\n", answer - 1);
}
};
int main() {
Solution().Solve();
return 0;
}
77. (977E) Cyclic Components
判断环,就是一个连通分量的每个点度都是 2 即可。
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N, M;
vector<vector<int> > Out;
vector<bool> Visited;
vector<int> CurrentComponent;
void DFS(int no) {
Visited[no] = true;
CurrentComponent.push_back(no);
for (int i = 0; i < Out[no].size(); ++i) {
int target = Out[no][i];
if (!Visited[target]) DFS(target);
}
}
bool IsCycle() {
for (int i = 0; i < CurrentComponent.size(); ++i) {
if (Out[CurrentComponent[i]].size() != 2) return false;
}
return true;
}
void Solve() {
scanf("%d%d", &N, &M);
Out.resize(N);
for (int i = 0; i < M; ++i) {
int v1, v2;
scanf("%d%d", &v1, &v2);
Out[v1 - 1].push_back(v2 - 1);
Out[v2 - 1].push_back(v1 - 1);
}
Visited.resize(N);
int answer = 0;
for (int i = 0; i < N; ++i) {
if (!Visited[i]) {
DFS(i);
if (IsCycle()) ++answer;
CurrentComponent.clear();
}
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
78. (158C) Cd and pwd commands
使用 vector<string> 存储路径即可。需要编写一个按照斜杠分割路径的子程序。
#include <cstdio>
#include <string>
#include <vector>
using namespace std;
char Buffer[201];
struct Solution {
vector<string> Path;
int N;
vector<string> SplitPath(const string& path) {
vector<string> answer;
int p = 0;
while (p < path.size()) {
if (path[p] == '/') ++p;
int nchar = 0;
if (sscanf(&path[p], "%[^/]%n", Buffer, &nchar) == 1) {
answer.push_back(Buffer);
p += nchar;
}
}
return answer;
}
void Solve() {
scanf("%d", &N);
for (int i = 0; i < N; ++i) {
scanf("%s", Buffer);
string command = Buffer;
if (command == "pwd") {
for (int i = 0; i < Path.size(); ++i) {
printf("/%s", Path[i].c_str());
}
printf("/\n");
} else {
scanf("%s", Buffer);
string newPath = Buffer;
if (newPath[0] == '/') Path.clear();
vector<string> newSplitted = SplitPath(newPath);
for (int i = 0; i < newSplitted.size(); ++i) {
if (newSplitted[i] == "..") {
Path.pop_back();
} else {
Path.push_back(newSplitted[i]);
}
}
}
}
}
};
int main() {
Solution().Solve();
return 0;
}
79. (788A) Functions again
先把原数组差分,取绝对值,转换成一个类似最大连续子段和的动态规划问题。
#include <cstdio>
#include <cmath>
#include <climits>
#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;
vector<int> A;
void Solve() {
scanf("%d", &N);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
for (int i = 0; i < N - 1; ++i) A[i] = abs(A[i + 1] - A[i]);
int64 answer = INT_MIN;
if (1 <= N - 1) {
int64 answer1 = A[0], current1 = A[0];
for (int i = 2; i < N - 1; i += 2) {
current1 -= A[i - 1];
current1 = current1 > 0 ? current1 + A[i] : A[i];
AssignMax(&answer1, current1);
}
AssignMax(&answer, answer1);
}
if (2 <= N - 1) {
int64 answer2 = A[1], current2 = A[1];
for (int i = 3; i < N - 1; i += 2) {
current2 -= A[i - 1];
current2 = current2 > 0 ? current2 + A[i] : A[i];
AssignMax(&answer2, current2);
}
AssignMax(&answer, answer2);
}
printf("%lld\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
80. (165B) Burning Midnight Oil
二分答案即可,其余计算过程均可暴力计算。
#include <cstdio>
using namespace std;
struct Solution {
int N, K;
int ComputeLines(int init) {
int answer = init;
while (init != 0) {
init /= K;
answer += init;
}
return answer;
}
int BinarySearch(int lower, int upper) {
while (lower < upper) {
int mid = lower + (upper - lower) / 2;
if (N <= ComputeLines(mid)) {
upper = mid;
} else {
lower = mid + 1;
}
}
return lower;
}
void Solve() {
scanf("%d%d", &N, &K);
int answer = BinarySearch(0, N);
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
81. (614A) Link/Cut Tree
直接比较会超过 int64 的范围。注意到 等价于
,而
等价于
。
#include <cstdio>
using namespace std;
typedef long long int64;
struct Solution {
int64 L, R, K;
void Solve() {
scanf("%lld%lld%lld", &L, &R, &K);
bool head = true;
if (L == 1) {
if (!head) putchar(' ');
head = false;
printf("1");
}
int64 p = 1;
while (p < (L + K - 1) / K) p *= K;
while (p <= R / K) {
if (!head) putchar(' ');
head = false;
printf("%lld", p * K);
p *= K;
}
if (head) {
printf("-1\n");
} else {
printf("\n");
}
}
};
int main() {
Solution().Solve();
return 0;
}
82. (445B) DZY Loves Chemistry
DFS 求连通分量的个数。
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N, M;
vector<vector<int> > Out;
vector<bool> Visited;
void DFS(int no) {
Visited[no] = true;
for (int i = 0; i < Out[no].size(); ++i) {
int target = Out[no][i];
if (!Visited[target]) DFS(target);
}
}
void Solve() {
scanf("%d%d", &N, &M);
Out.resize(N);
for (int i = 0; i < M; ++i) {
int v1, v2;
scanf("%d%d", &v1, &v2);
Out[v1 - 1].push_back(v2 - 1);
Out[v2 - 1].push_back(v1 - 1);
}
Visited.resize(N);
int answer = 0;
for (int i = 0; i < N; ++i) {
if (!Visited[i]) {
++answer;
DFS(i);
}
}
printf("%lld\n", 1LL << (N - answer));
}
};
int main() {
Solution().Solve();
return 0;
}
83. (557B) Pasha and Tea
将输入排序,小的一半用来给女生喝水就可以了。请尝试用调整法证明其正确性。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
bool AssignMin(double* p, double v) {
if (v < *p) return *p = v, true;
return false;
}
struct Solution {
int N, W;
vector<int> A;
void Solve() {
scanf("%d%d", &N, &W);
A.resize(N + N);
for (int i = 0; i < N + N; ++i) scanf("%d", &A[i]);
sort(A.begin(), A.end());
double minCup1 = 1.0 / 0.0, minCup2 = 1.0 / 0.0;
for (int i = 0; i < N; ++i) AssignMin(&minCup1, A[i]);
for (int i = 0; i < N; ++i) AssignMin(&minCup2, A[N + i]);
double minCup = min(minCup1, minCup2 * 0.5);
double capacity = minCup * 3.0 * N;
printf("%.12lf\n", min(capacity, double(W)));
}
};
int main() {
Solution().Solve();
return 0;
}
84. (1009B) Minimum Ternary String
题意可以这么理解,1 可以任意穿透 0 和 2,但 0 和 2 的相对位置不变。因此先删除所有的 1,然后放在最前面的一堆 0 后面就可以了。
#include <algorithm>
#include <cstdio>
#include <string>
using namespace std;
char Buffer[100001];
struct Solution {
string S, S1, S2;
void Solve() {
scanf("%s", Buffer);
S = Buffer;
S1.reserve(S.size());
int one = 0;
for (int i = 0; i < S.size(); ++i) {
if (S[i] == '1') {
++one;
} else {
S1 += S[i];
}
}
int p = 0;
while (p < S1.size() && S1[p] == '0') ++p;
S2 = S1.substr(0, p);
S2.reserve(S.size());
for (int i = 0; i < one; ++i) S2 += '1';
S2 += S1.substr(p);
printf("%s\n", S2.c_str());
}
};
int main() {
Solution().Solve();
return 0;
}
85. (763A) Timofey and a tree
先随便找一个点把树变成有根树。然后计算出每个节点的子树的颜色(0 为杂色),再判断每个节点能否作为题目中要求的根,即删除它后得到的森林里面每棵树都是同色。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<vector<int> > Out;
vector<int> C, Subtree;
void Hang(int no, int parent) {
for (int i = 0; i < Out[no].size(); ++i) {
if (Out[no][i] == parent) {
swap(Out[no][0], Out[no][i]);
} else {
Hang(Out[no][i], no);
}
}
}
void ComputeSubtree(int no) {
Subtree[no] = C[no];
for (int i = 1; i < Out[no].size(); ++i) {
int target = Out[no][i];
ComputeSubtree(target);
if (Subtree[target] != Subtree[no]) Subtree[no] = 0;
}
}
int FindGoodRoot(int no, int upColor) {
bool good = (upColor != 0);
for (int i = 1; i < Out[no].size(); ++i) {
int target = Out[no][i];
if (Subtree[target] == 0) good = false;
}
if (good) return no;
if (upColor != C[no]) return -1;
int different = 0;
for (int i = 1; i < Out[no].size(); ++i) {
int target = Out[no][i];
if (Subtree[target] != upColor) ++different;
}
for (int i = 1; i < Out[no].size(); ++i) {
int target = Out[no][i];
if (Subtree[target] != upColor) --different;
if (different == 0) {
int ret = FindGoodRoot(target, upColor);
if (ret != -1) return ret;
}
if (Subtree[target] != upColor) ++different;
}
return -1;
}
void Solve() {
scanf("%d", &N);
Out.resize(N);
for (int i = 1; i < N; ++i) {
int v1, v2;
scanf("%d%d", &v1, &v2);
Out[v1 - 1].push_back(v2 - 1);
Out[v2 - 1].push_back(v1 - 1);
}
C.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &C[i]);
Out[0].push_back(-1);
Hang(0, -1);
Subtree.resize(N);
ComputeSubtree(0);
int ret = FindGoodRoot(0, C[0]);
if (ret == -1) {
printf("NO\n");
} else {
printf("YES\n%d\n", ret + 1);
}
}
};
int main() {
Solution().Solve();
return 0;
}
86. (414A) Mashmokh and Numbers
除了第一对数,令剩下的最大公约数都为 1。需要凑一凑满足数字两两不同的条件。
#include <cstdio>
using namespace std;
struct Solution {
int N, K;
void Solve() {
scanf("%d%d", &N, &K);
int n1 = N / 2;
if (N == 1) {
printf(K == 0 ? "1\n" : "-1\n");
} else if (K < n1) {
printf("-1\n");
} else {
int a1 = K - (n1 - 1);
printf("%d %d", a1 * 2, a1 * 3);
const int m = 400000000;
for (int i = 1; i < n1; ++i) {
printf(" %d %d", m + i + i, m + i + i + 1);
}
if (N % 2 != 0) printf(" 1");
printf("\n");
}
}
};
int main() {
Solution().Solve();
return 0;
}
87. (587A) Duff and Weight Lifting
如果最小的杠铃有奇数个,必须先拿一个。否则可以两两配对后把杠铃的大小加一,数量除以二,答案不变。
#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(*max_element(A.begin(), A.end()) + 1);
for (int i = 0; i < N; ++i) ++B[A[i]];
int answer = 0, carry = 0;
for (int i = 0; i < B.size(); ++i) {
carry += B[i];
answer += carry % 2;
carry /= 2;
}
while (carry != 0) {
answer += carry % 2;
carry /= 2;
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
88. (567C) Geometric Progression
枚举中间的数,相当于查询某数左边等于 的数有多少个,右边等于
的数有多少个,可以使用 map 实现。
#include <cstdio>
#include <map>
#include <vector>
using namespace std;
typedef long long int64;
struct Solution {
int N, K;
vector<int> A, B1, B2;
map<int64, int> M1, M2;
void Solve() {
scanf("%d%d", &N, &K);
A.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &A[i]);
B1.resize(N);
for (int i = 0; i < N; ++i) {
if (A[i] % K == 0) {
B1[i] = M1[A[i] / K];
} else {
B1[i] = 0;
}
++M1[A[i]];
}
M1.clear();
B2.resize(N);
for (int i = N - 1; i >= 0; --i) {
B2[i] = M2[A[i] * int64(K)];
++M2[A[i]];
}
M2.clear();
int64 answer = 0;
for (int i = 0; i < N; ++i) answer += int64(B1[i]) * B2[i];
printf("%lld\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
89. (372A) Counting Kangaroos is Fun
运用调整法可以证明,只要尝试把前一半的袋鼠放进后一半的袋鼠里面就可以了。
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
struct Solution {
int N;
vector<int> S;
void Solve() {
scanf("%d", &N);
S.resize(N);
for (int i = 0; i < N; ++i) scanf("%d", &S[i]);
sort(S.begin(), S.end());
int i2 = N / 2, answer = N - N / 2;
for (int i = 0; i < N / 2; ++i) {
while (i2 < N && S[i2] < S[i] + S[i]) ++i2;
if (i2 < N) {
++i2;
} else {
++answer;
}
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
90. (546D) Soldier and Number Game
求质因数个数,需要先用筛法求出每个数的最小质因数,然后动态规划。
#include <cstdio>
#include <vector>
using namespace std;
typedef long long int64;
bool AssignMax(int* p, int v) {
if (*p < v) return *p = v, true;
return false;
}
struct Query {
int A, B;
};
struct Solution {
int N, M;
vector<Query> Queries;
vector<int> MinFactor, NumFactor;
void Solve() {
scanf("%d", &N);
Queries.resize(N);
int M = 0;
for (int i = 0; i < N; ++i) {
scanf("%d%d", &Queries[i].A, &Queries[i].B);
AssignMax(&M, Queries[i].A);
}
MinFactor.resize(M + 1);
for (int i = 2; i <= M; ++i) {
if (MinFactor[i] == 0) {
MinFactor[i] = i;
for (int64 j = int64(i) * i; j <= M; j += i) {
if (MinFactor[j] == 0) MinFactor[j] = i;
}
}
}
NumFactor.resize(M + 1);
for (int i = 2; i <= M; ++i) {
NumFactor[i] = NumFactor[i / MinFactor[i]] + 1;
}
for (int i = 1; i <= M; ++i) NumFactor[i] += NumFactor[i - 1];
for (int i = 0; i < N; ++i) {
printf("%d\n", NumFactor[Queries[i].A] - NumFactor[Queries[i].B]);
}
}
};
int main() {
Solution().Solve();
return 0;
}
91. (322B) Ciel and Flowers
如果有 3 个以上的混合花束,可以把 3 个混合花束替换成 3 个各种花束。所以存在一个最优答案,其中混合花束的数量不超过 2。枚举这个数量即可。
#include <algorithm>
#include <cstdio>
using namespace std;
bool AssignMax(int* p, int v) {
if (*p < v) return *p = v, true;
return false;
}
struct Solution {
int R, G, B;
void Solve() {
scanf("%d%d%d", &R, &G, &B);
int minRGB = min(R, min(G, B));
int answer = 0;
for (int i = 0; i < 3; ++i) {
if (minRGB < i) break;
int r = (R - i) / 3;
int g = (G - i) / 3;
int b = (B - i) / 3;
AssignMax(&answer, r + g + b + i);
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
92. (538A) Cutting Banner
计算开始有多少字符匹配,末尾处有多少字符匹配,如果两者之和能覆盖 CODEFORCES 就可以。
#include <algorithm>
#include <cstdio>
#include <string>
using namespace std;
const string C = "CODEFORCES";
char Buffer[101];
struct Solution {
string S;
int MaxMatch(const string& s1, const string& s2) {
for (int i = 0; ; ++i) {
if (i == s1.size()) return i;
if (i == s2.size()) return i;
if (s1[i] != s2[i]) return i;
}
}
void Solve() {
scanf("%s", Buffer);
S = Buffer;
int match1 = MaxMatch(S, C);
string S2 = S, C2 = C;
reverse(S2.begin(), S2.end());
reverse(C2.begin(), C2.end());
int match2 = MaxMatch(S2, C2);
printf(match1 + match2 >= C.size() ? "YES\n" : "NO\n");
}
};
int main() {
Solution().Solve();
return 0;
}
93. (505B) Mr. Kitayuta's Colorful Graph
使用 DFS 计算出每种颜色的连通分量,然后直接统计即可。
#include <cstdio>
#include <vector>
using namespace std;
struct Graph {
vector<vector<int> > Out;
vector<int> Component;
int ComponentSize;
void AddEdge(int no1, int no2) {
Out[no1].push_back(no2);
Out[no2].push_back(no1);
}
void DFS(int no, int current) {
Component[no] = current;
for (int i = 0; i < Out[no].size(); ++i) {
int target = Out[no][i];
if (Component[target] == 0) DFS(target, current);
}
}
void ComputeConnectedComponents() {
ComponentSize = 0;
Component.resize(Out.size());
for (int i = 0; i < Out.size(); ++i) {
if (Component[i] == 0) {
DFS(i, ++ComponentSize);
}
}
}
bool SameComponent(int no1, int no2) {
return Component[no1] == Component[no2];
}
};
struct Solution {
int N, M, Q;
vector<Graph> Graphs;
void Solve() {
scanf("%d%d", &N, &M);
Graphs.resize(M);
for (int i = 0; i < M; ++i) Graphs[i].Out.resize(N);
for (int i = 0; i < M; ++i) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
Graphs[c - 1].AddEdge(a - 1, b - 1);
}
for (int i = 0; i < M; ++i) Graphs[i].ComputeConnectedComponents();
scanf("%d", &Q);
for (int i = 0; i < Q; ++i) {
int u1, u2;
scanf("%d%d", &u1, &u2);
int answer = 0;
for (int j = 0; j < M; ++j) {
if (Graphs[j].SameComponent(u1 - 1, u2 - 1)) ++answer;
}
printf("%d\n", answer);
}
}
};
int main() {
Solution().Solve();
return 0;
}
94. (1200C) Round Corridor
里外都有墙就无法通过,否则可以通过。里外都有墙的地方共有 个,分割成这么多个区域,只要判断起点和终点在不在同一个区域即可。
#include <cstdio>
using namespace std;
typedef long long int64;
int64 GCD(int64 a, int64 b) {
if (b == 0) return a;
return GCD(b, a % b);
}
struct Solution {
int64 N, M, G;
int Q;
void Solve() {
scanf("%lld%lld%d", &N, &M, &Q);
G = GCD(N, M);
for (int i = 0; i < Q; ++i) {
int sx, ex;
int64 sy, ey;
scanf("%d%lld%d%lld", &sx, &sy, &ex, &ey), --sy, --ey;
sy /= (sx == 1 ? N : M) / G;
ey /= (ex == 1 ? N : M) / G;
if (sy == ey) {
printf("YES\n");
} else {
printf("NO\n");
}
}
}
};
int main() {
Solution().Solve();
return 0;
}
95. (1010A) Fly
最后一定没多余的燃料。从最后出发倒推,就可以得出初始需要的燃料。
#include <cstdio>
using namespace std;
struct Solution {
int N, M, X;
void Solve() {
scanf("%d%d", &N, &M);
double answer = M;
for (int i = 0; i < N + N; ++i) {
scanf("%d", &X);
answer *= X / double(X - 1);
}
if (answer > 1e10) {
printf("-1\n");
} else {
printf("%.12lf\n", answer - M);
}
}
};
int main() {
Solution().Solve();
return 0;
}
96. (448D) Multiplication Table
题目有误:输出第 小而不是第
大的数。
二分答案,然后 计算不比
大的数的个数即可。
#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long int64;
struct Solution {
int64 N, M, K;
int64 CountNoMoreThan(int64 x) {
int64 answer = 0;
for (int64 i = 1; i <= N; ++i) {
answer += min(x / i, M);
}
return answer;
}
int64 BinarySearch(int64 lower, int64 upper) {
while (lower < upper) {
int64 mid = lower + (upper - lower) / 2;
if (CountNoMoreThan(mid) < K) {
lower = mid + 1;
} else {
upper = mid;
}
}
return lower;
}
void Solve() {
scanf("%lld%lld%lld", &N, &M, &K);
printf("%lld\n", BinarySearch(1, N * M));
}
};
int main() {
Solution().Solve();
return 0;
}
97. (468A) 24 Game
用前几个数凑 ,后面的数两两减成一然后乘到答案上去。
#include <cstdio>
using namespace std;
struct Solution {
int N;
void Solve() {
scanf("%d", &N);
if (N <= 3) {
printf("NO\n");
} else if (N % 2 == 0) {
printf("YES\n");
printf("1 * 2 = 2\n");
printf("2 * 3 = 6\n");
printf("6 * 4 = 24\n");
for (int i = 5; i < N; i += 2) {
printf("%d - %d = 1\n", i + 1, i);
printf("24 * 1 = 24\n");
}
} else {
printf("YES\n");
printf("1 * 2 = 2\n");
printf("3 + 4 = 7\n");
printf("7 + 5 = 12\n");
printf("2 * 12 = 24\n");
for (int i = 6; i < N; i += 2) {
printf("%d - %d = 1\n", i + 1, i);
printf("24 * 1 = 24\n");
}
}
}
};
int main() {
Solution().Solve();
return 0;
}
98. (282B) Painting Eggs
每分配一个蛋,都满足两者之差小于等于 500。运用归纳法,可以证明这样做是可行的。
#include <cstdio>
using namespace std;
struct Solution {
int N, A, G;
void Solve() {
scanf("%d", &N);
int difference = 0;
for (int i = 0; i < N; ++i) {
scanf("%d%d", &A, &G);
if (difference + A <= 500) {
putchar('A');
difference += A;
} else {
putchar('G');
difference -= G;
}
}
putchar('\n');
}
};
int main() {
Solution().Solve();
return 0;
}
99. (555A) Case of Matryoshkas
除了一个从 1 开始的连续链条可以不拆之外,别的都需要拆。
#include <cstdio>
using namespace std;
struct Solution {
int N, K;
void Solve() {
scanf("%d%d", &N, &K);
int answer = N + N - K + 1;
for (int i = 0; i < K; ++i) {
int length;
scanf("%d", &length);
for (int j = 0; j < length; ++j) {
int a;
scanf("%d", &a);
if (a == j + 1) answer -= 2;
}
}
printf("%d\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}
100. (453A) Little Pony and Expected Maximum
计算出最大的数至多为 的情况数,减去最大的数至多为
的情况数,就是最大的数恰好是
的情况数。
#include <cstdio>
#include <cmath>
using namespace std;
struct Solution {
int M, N;
void Solve() {
scanf("%d%d", &M, &N);
double answer = 0.0;
for (int i = 1; i <= M; ++i) {
answer += i * (pow(double(i) / M, N) - pow(double(i - 1) / M, N));
}
printf("%.12lf\n", answer);
}
};
int main() {
Solution().Solve();
return 0;
}