A. 简单判断
题意:给定一个字符串,判断是以 e r er er 结尾还是以 i s t ist ist 结尾
判断最后一个字母是 r r r 还是 t t t 即可
#include<bits/stdc++.h>
using namespace std;
string s;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> s;
if (s.back() == 'r') cout << "er\n";
else cout << "ist\n";
return 0;
}
B. 暴力枚举
题意:给定一个 H × W H\times W H×W 的矩阵,判断对于所有的 1 ≤ i 1 ≤ i 2 ≤ H , 1 ≤ j 1 ≤ j 2 ≤ W 1\leq i_1\leq i_2\leq H, 1\leq j_1\leq j_2\leq W 1≤i1≤i2≤H,1≤j1≤j2≤W,是否满足 A i 1 , j 1 + A i 2 , j 2 ≤ A i 2 , j 1 + A i 1 , j 2 A_{i_1, j_1} + A_{i_2, j_2}\leq A_{i_2, j_1} + A_{i_1, j_2} Ai1,j1+Ai2,j2≤Ai2,j1+Ai1,j2
暴力枚举所有的 i 1 , i 2 , j 1 , j 2 i_1, i_2, j_1, j_2 i1,i2,j1,j2 即可,时间复杂度: O ( H 2 W 2 ) O(H^2W^2) O(H2W2)
#include<bits/stdc++.h>
using namespace std;
int n, m;
const int N = 55;
int a[N][N];
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}
for (int i1 = 1; i1 <= n; i1++) {
for (int i2 = i1 + 1; i2 <= n; i2++) {
for (int j1 = 1; j1 <= m; j1++) {
for (int j2 = j1 + 1; j2 <= m; j2++) {
if (a[i1][j1] + a[i2][j2] > a[i2][j1] + a[i1][j2]) {
cout << "No\n";
return 0;
}
}
}
}
}
cout << "Yes\n";
return 0;
}
C. 暴力枚举
题意:给定 N N N 个不重复的点,判断能组成多少个三角形
因为 N ≤ 300 N\leq 300 N≤300 这一条件,所以我们可以直接暴力 O ( N 3 ) O(N^3) O(N3) 枚举所有的点组合,并判断每个点组合是否可行即可
判断 ( x i , y i ) , ( x j , y j ) , ( x k , y k ) (x_i, y_i), (x_j, y_j), (x_k, y_k) (xi,yi),(xj,yj),(xk,yk) 是否可行,即判断其是否共线
- 令 d 1 = ( x j − x i , y j − y i ) , d 2 = ( x k − x i , y k − y i ) d_1 = (x_j - x_i, y_j - y_i), d_2 = (x_k - x_i, y_k - y_i) d1=(xj−xi,yj−yi),d2=(xk−xi,yk−yi)
- 如果 d 1 , d 2 d_1, d_2 d1,d2 共线,那么 d 1 × d 2 = ( x j − x i ) ( y k − y i ) − ( y j − y i ) ( x k − x i ) = 0 d_1 \times d_2 = (x_j - x_i)(y_k - y_i) - (y_j - y_i)(x_k - x_i) = 0 d1×d2=(xj−xi)(yk−yi)−(yj−yi)(xk−xi)=0,注意这里是向量叉积,我们只需要判断上式是否成立即可
- 反之不共线
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e2 + 5;
int n, x[N], y[N];
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> x[i] >> y[i];
}
ll cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
for (int k = j + 1; k <= n; k++) {
int dx1 = x[j] - x[i], dy1 = y[j] - y[i];
int dx2 = x[k] - x[i], dy2 = y[k] - y[i];
cnt += 1ll * dx1 * dy2 - 1ll * dx2 * dy1 != 0;
}
}
}
cout << cnt << '\n';
return 0;
}
D. 暴力模拟
题意:8-puzzle 问题的变种,求将 1 1 1 到 8 8 8 编号的棋子放到 1 1 1 到 9 9 9 编号的节点上的最小步数,同一时间每个节点最多只能放 1 1 1 个棋子,节点之间存在转移边
总状态数为 9 ! 9! 9!,转移数最大为 9 9 9,所以暴力模拟的复杂度为 O ( 9 × 9 ! ) O(9\times 9!) O(9×9!),需要记录当前状态是否被走过
因为需要求的是最小操作次数,所以需要 b f s bfs bfs 而不能用 d f s dfs dfs,具体细节见代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 55;
int m, vis[N];
vector<int> nxt[N];
map<vector<int>, int> d;
vector<int> tar = {0, 1, 2, 3, 4, 5, 6, 7, 8};
void bfs(vector<int> p) {
queue<vector<int>> quu;
quu.push(p);
d[p] = 0;
while (!quu.empty()) {
vector<int> now = quu.front(); quu.pop();
if (now == tar) {
cout << d[now] << '\n';
return;
}
fill(vis, vis + 10, 0);
for (int i = 1; i <= 8; i++) {
vis[now[i]] = 1;
}
for (int i = 1; i <= 8; i++) {
int pos = now[i], step = d[now];
for (auto it: nxt[pos]) {
if (vis[it]) continue;
vis[pos] = 0;
vis[it] = 1;
now[i] = it;
if (!d.count(now)) {
d[now] = step + 1;
quu.push(now);
}
now[i] = pos;
vis[pos] = 1;
vis[it] = 0;
}
}
}
cout << -1 << '\n';
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> m;
for (int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
nxt[u].push_back(v);
nxt[v].push_back(u);
}
vector<int> p(9, 0);
for (int i = 1; i <= 8; i++) {
cin >> p[i];
}
bfs(p);
return 0;
}
E. 动态规划
题意:在一个方格上走,每次只能走同一行或同一列,且到达的格点上的值必须严格大于当前值,问最多走多少步
我们可以按行和列进行 d p dp dp
令 r o w [ i ] row[i] row[i] 表示当前第 i i i 行的数向后最多能走多少步, c o l [ i ] col[i] col[i] 表示当前第 i i i 列的数向后最多能走多少步
可以将数从大向小加入方格中,维护 r o w [ i ] row[i] row[i] 和 c o l [ i ] col[i] col[i] 的变化
对于即将加入个点的数 ( r , c , a ) (r, c, a) (r,c,a),他的贡献是 r o w [ r ] ← r o w [ r ] + 1 , c o l [ c ] ← c o l [ c ] + 1 row[r] \leftarrow row[r] + 1, col[c]\leftarrow col[c] + 1 row[r]←row[r]+1,col[c]←col[c]+1
需要注意的是,移动到的点必须是值严格大于他的,所以不能一个数一个数去加,而应该对于具有相同值的数一起加,再统一更新
时间复杂度: O ( N log N ) O(N\log N) O(NlogN)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int h, w, n, row[N], col[N], r[N], c[N], a[N], ans[N];
map<int, vector<int>> mp;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> h >> w >> n;
for (int i = 1; i <= n; i++) {
cin >> r[i] >> c[i] >> a[i];
mp[-a[i]].push_back(i);
}
for (auto it: mp) {
vector<int>& now = it.second;
for (auto id: now) {
ans[id] = max(row[r[id]], col[c[id]]);
}
for (auto id: now) {
row[r[id]] = max(row[r[id]], ans[id] + 1);
col[c[id]] = max(col[c[id]], ans[id] + 1);
}
}
for (int i = 1; i <= n; i++) {
cout << ans[i] << '\n';
}
return 0;
}
F. 数学公式推导
题意:给定一个数字串,向其中随意放入加号组成表达式,问所有情况的表达式值的和是多少
很显然这是一个推公式的题
假如当前是第 i i i 位,当前数是第 j j j 位,这样的情况数有多少种呢
- i + j , i + j + 1 , . . . , N i + j, i + j + 1, ... , N i+j,i+j+1,...,N 随意分配加号,情况数为 2 N − i − j 2^{N - i - j} 2N−i−j 种,需要注意的是 i + j − 1 = N i + j - 1 = N i+j−1=N 时情况数为 1 1 1,这种情况分开讨论
- 1 , 2 , . . . , i − 1 1, 2, ..., i - 1 1,2,...,i−1 随意分配加号, i − 1 i - 1 i−1 后面可分配可不分配,一共 2 i − 1 2^{i - 1} 2i−1 种情况
那么,答案表达式为:
KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ ans &= \sum\li…
对于
∑
i
=
1
N
c
i
×
(
2
i
−
1
×
1
0
N
−
i
)
\sum\limits_{i = 1}^{N}c_i \times(2^{i - 1}\times 10^{N - i})
i=1∑Nci×(2i−1×10N−i) 可以
O
(
N
)
O(N)
O(N) 求
对于 ∑ i = 1 N c i × ∑ j = 1 N − i 2 N − j − 1 × 1 0 j − 1 \sum\limits_{i = 1}^{N}c_i\times \sum\limits_{j = 1}^{N - i}2^{N - j - 1}\times 10^{j - 1} i=1∑Nci×j=1∑N−i2N−j−1×10j−1,其实不难发现 ∑ j = 1 N − i 2 N − j − 1 × 1 0 j − 1 \sum\limits_{j = 1}^{N - i}2^{N - j - 1}\times 10^{j - 1} j=1∑N−i2N−j−1×10j−1 是一个表达式内与 i i i 无关的量,我们只需要预处理出 ∑ j = 1 q 2 N − j − 1 × 1 0 j − 1 \sum\limits_{j = 1}^{q}2^{N - j - 1}\times 10^{j - 1} j=1∑q2N−j−1×10j−1 的表即可,后续就可以直接 O ( 1 ) O(1) O(1) 查了,所以复杂度也是 O ( N ) O(N) O(N)
总时间复杂度: O ( N ) O(N) O(N)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, mod = 998244353;
char s[N];
int n, ans, pw2[N], pw10[N], v[N];
int main(void) {
scanf("%s", s + 1);
n = strlen(s + 1);
pw2[0] = pw10[0] = 1;
for (int i = 1; i <= n; i++) {
pw2[i] = 1ll * pw2[i - 1] * 2 % mod;
pw10[i] = 1ll * pw10[i - 1] * 10 % mod;
}
for (int i = 1; i < n; i++) {
v[i] += (v[i - 1] + 1ll * pw2[n - i - 1] * pw10[i - 1]) % mod;
}
for (int i = 1; i <= n; i++) {
int c = s[i] - '0';
int tmp = 0;
for (int j = 1; j <= n - i; j++) {
tmp += 1ll * pw2[n - j - 1] % mod * pw10[j - 1] % mod;
tmp %= mod;
}
ans += 1ll * c * v[n - i] % mod; ans %= mod;
ans += 1ll * pw2[2, i - 1] * c % mod * pw10[10, n - i] % mod; ans %= mod;
}
cout << ans << '\n';
return 0;
}
G. 概率论
题意:给定起点 S S S 与终点 T T T,可以做两个操作
- S ← S + 1 S\leftarrow S+1 S←S+1,代价为 A A A
- 将 S S S 随机变为 [ 1 , N ] [1, N] [1,N] 的随机一个整数,代价为 B B B
问在最优策略下,从 S S S 走到 T T T 的期望代价最小是多少
关键在于选什么样的策略
有两种可能的情况:
- 先随机走,如果随机到一个比较好的值,就一直加到 T T T
- 如果满足 S ≤ T S\leq T S≤T,的话可以直接加到 T T T
不难发现,这两种做法实际上是完全独立的,也就是说不可能出现先加再随机的情况(这肯定不好),在一开始就要选定是用两种策略里的哪一种
事实上,我们只需要计算两种策略的最小值就是答案
对于第二种情况很简单,就是 ( T − S ) × A (T - S)\times A (T−S)×A
对于第一种情况,一定是有一个阈值 x , ( x ≤ T ) x,(x\leq T) x,(x≤T),如果随机到的数满足 [ x , T ] [x, T] [x,T],就一直加到 T T T,反之继续随机
随机到 [ x , T ] [x, T] [x,T] 的期望步数为 N T − x + 1 \frac{N}{T - x + 1} T−x+1N,期望值为 N T − x + 1 B \frac{N}{T - x + 1}B T−x+1NB
对已随机到 [ x , T ] [x, T] [x,T] 的所有情况,其到达 T T T 的数学期望为 0 + 1 + . . . + T − x T − x + 1 = T − x 2 \frac{0 + 1 + ... + T - x}{T - x + 1} = \frac{T - x}{2} T−x+10+1+...+T−x=2T−x,期望值为 T − x 2 A \frac{T - x}{2}A 2T−xA
总期望为 N T − x + 1 B + T − x 2 A \frac{N}{T -x + 1}B + \frac{T - x}{2}A T−x+1NB+2T−xA,我们需要选定一个合适的 x x x,来使这个表达式最小
不难发现上述表达是是一个钩型函数 x + a x x + \frac{a}{x} x+xa 的形式,所以我们可以算出其解析解,或者三分求最小值
将表达式的最小值与情况 2 2 2 的值取最小值即为答案
时间复杂度: O ( 1 ) O(1) O(1)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
int n, s, t, a, b;
double f(int x) {
return 1.0 * a / 2 * (t - x) + 1.0 * n * b / (t - x + 1);
}
double bin3(void) {
int l = 1, r = t;
double ret = min(f(l), f(r));
while (l <= r) {
int m = (l + r) / 2, mm = (m + r) / 2;
double fm = f(m), fmm = f(mm);
if (fm >= fmm) l = m + 1;
else r = mm - 1;
ret = min(ret, min(fm, fmm));
}
return ret;
}
signed main(void) {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> s >> t >> a >> b;
cout.precision(50);
double ans = bin3();
if (s <= t) ans = min(ans, 1.0 * (t - s) * a);
cout << ans << endl;
return 0;
}