A 简单的背包
这题如果用传统01背包是将 f[i][j]
看作前 i
个物品在重量为 j
时的最大价值,复杂度极高,不可取
当初也是想让 f[i][j]
表示前 i
个物品在价值为 j
时的最小重量,复杂度达到了 1e7
,故扔了这题 <= 纯🤡了
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e3 + 2, V = 1e5 + 2;
ll n, weight;
ll w[N], v[N];
ll f[N][V];
int main() {
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
cin >> n >> weight;
for (int i = 1; i <= n; i ++) cin >> w[i] >> v[i];
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= V; j ++) {
f[i][j] = f[i - 1][j];
if (j >= v[i])
f[i][j] = min(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
}
int res = 0;
for (int i = 0; i < V; i ++) {
if (f[n][i] <= weight) res = i;
}
cout << res << endl;
return 0;
}
B 修路专家 AC
先 sort
再逐步更新 idx
,遍历一遍即可
#include <iostream>
#include <vector>
#include <algorithm>
#define aa first
#define bb second
using namespace std;
typedef pair<int, int> pii;
const int N = 1e4 + 2;
int n, L;
vector<pii> v;
int main() {
cin >> n >> L;
for (int i = 0; i < n; i ++) {
int s, e; cin >> s >> e;
v.push_back({s, e});
}
sort(v.begin(), v.end());
int res = 0;
int idx = v[0].aa;
for (int i = 0; i < n; i ++) {
int l = v[i].aa, r = v[i].bb;
if (idx < l) {
idx = l;
} else if (idx >= r) {
continue;
}
int newadd = (r - idx + L - 1) / L;
res += newadd;
idx += newadd * L;
}
cout << res << endl;
return 0;
}
C 小小园艺师
没错,🤡又是我了,n
个节点的树,只有 n - 1
条边,俺 for
循环了 n
次😅,少了个 memset
感谢叶佬,拯救我于水火之中,这题还要特判 n == 1
的情况
从树枝开始遍历,将与树枝相连的放入队列中,当一个点的度数为零时候从队列移除
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 4e5 + 2, M = 8e5 + 2;
int idx, e[M], ne[M], h[N];
int d[N], a[N]; // 点数,被第几次删除
int n, k;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++, d[b] ++;
}
int solve() {
cin >> n >> k;
if (n == 1) {
if (k) {
return 0;
}
return 1;
}
for (int i = 1; i < n; i ++) {
int a, b; cin >> a >> b;
add(a, b), add(b, a);
}
queue<int> q;
for (int i = 1; i <= n; i ++) {
if (d[i] == 1) {
a[i] = 1;
q.push(i);
}
}
while (q.size()) {
int t = q.front();
q.pop();
for (int i = h[t]; i != -1; i = ne[i]) {
int x = e[i];
d[x] --;
if (d[x] == 1) {
q.push(x);
a[x] = a[t] + 1;
}
}
}
ll res = 0;
for (int i = 1; i <= n; i ++) {
if (a[i] > k) res ++;
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T; cin >> T;
while (T --) {
idx = 0;
memset(h, -1, sizeof h);
memset(d, 0, sizeof d);
cout << solve() << endl;
}
return 0;
}
D 跳格子 AC
签到题🦆
#include <iostream>
using namespace std;
int main() {
int T; cin >> T;
while (T --) {
int n, k; cin >> n >> k;
if (n % k) {
cout << 1 << endl << n << endl;
} else {
cout << 2 << endl << 1 << ' ' << n - 1 << endl;
}
}
return 0;
}
E 狼人杀但是逆天 AC
有 s[i]
个人说得数小于等于 i
,要使得 s[i] == n - i
,若成立则 i
就是答案,若遍历后无一成立则有人分不清自己身份
#include <iostream>
using namespace std;
const int N = 102;
int n;
int a[N];
int main() {
int T; cin >> T;
while (T --) {
cin >> n;
int b[N] = {0};
for (int i = 0; i < n; i ++) {
cin >> a[i];
b[a[i]] ++;
}
int s[N];
s[0] = b[0];
for (int i = 1; i <= n; i ++) s[i] = s[i - 1] + b[i];
int res = -1;
for (int i = 0; i <= n; i ++) {
if (s[i] == n - i) {
res = i;
break;
}
}
cout << res << endl;
}
return 0;
}
F 昊昊的难题 Hard version
状态转移表示
-
p[i]
表示前i
个字符,以字符p
结尾的子序列数量 -
np[i]
表示前i
个字符,不以字符p
结尾的子序列数量
状态计算
- 当
s[i] == 'p'
时,p[i] = (p[i - 1] * 2 + np[i - 1] + 1) % mod
,np[i] = np[i - 1]
- 不选
s[i - 1]
时,p[i] = p[i - 1]
,np[i] = np[i - 1]
- 选
s[i]
时,p[i] = p[i - 1] + np[i - 1] + 1
- 不选
- 当
s[i] == 'f'
时,p[i] = p[i - 1]
,np[i] = (np[i - 1] * 2 + 1) % mod
- 不选
s[i - 1]
时,p[i] = p[i - 1]
,np[i] = np[i - 1]
- 选
s[i]
时,np[i] = np[i - 1] + 1
- 不选
- 当
s[i] != 'p' && s[i] != 'f'
时,p[i] = p[i - 1]
,np[i] = (p[i - 1] + np[i - 1] * 2 + 1) % mod
- 不选
s[i - 1]
时,p[i] = p[i - 1]
,np[i] = np[i - 1]
- 选
s[i]
时,np[i] = p[i - 1] + np[i - 1] + 1
- 不选
#include <iostream>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int mod = 1e9 + 7, N = 1e5 + 2;
ll p[N], np[N];
int main() {
string s; cin >> s;
ll n = s.size();
for (int i = 0; i < n; i ++) {
if (s[i] == 'p') {
p[i] = (p[i - 1] * 2 + np[i - 1] + 1) % mod;
np[i] = np[i - 1];
} else if (s[i] == 'f') {
p[i] = p[i - 1];
np[i] = (np[i - 1] * 2 + 1) % mod;
} else {
p[i] = p[i - 1];
np[i] = (p[i - 1] + np[i - 1] * 2 + 1) % mod;
}
}
cout << (p[n - 1] + np[n - 1]) % mod << endl;
return 0;
}
G 昊昊的难题 Easy version AC
这题的子字符串需要满足连续,因此从头开始遍历,若 s[i]
和 s[i - 1]
能构成字符串 pf
,则前面(包含字符 p
)有 len = i - idx
个字符,因此可以组合出 (len + 1) * len / 2
个子字符串,并且更新 idx
为字符 f
的位置 i
#include <iostream>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
int main() {
string s; cin >> s;
int n = s.size();
ll res = 0, ans = 0;
int idx = 0;
for (int i = 1; i < n; i ++) {
if (s[i - 1] == 'p' && s[i] == 'f') {
ll len = i - idx;
ans = (len + 1) * len / 2;
res = (res + ans) % mod;
idx = i;
}
}
ll len = n - idx;
ans = (len + 1) * len / 2;
res = (res + ans) % mod;
cout << res << endl;
return 0;
}
H 同构
😅,前面写的 YES|NO
,样例是 Yes|No
,补题时一直 WA
才注意到
这题只用暴力枚举横纵分别平移 da
和 db
个单位,依次匹配是否一样即可
#include <iostream>
using namespace std;
const int N = 32;
char a[N][N], b[N][N];
int n, m;
bool check(int da, int db) {
for (int i = 0; i < n; i ++)
for (int j = 0; j < m; j ++)
if (a[(i + da) % n][(j + db) % m] != b[i][j])
return false;
return true;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i ++) cin >> a[i];
for (int i = 0; i < n; i ++) cin >> b[i];
for (int i = 0; i < n; i ++) {
for (int j = 0; j < n; j ++) {
if(check(i, j)) {
puts("YES");
return 0;
}
}
}
puts("NO");
return 0;
}
I 屠龙勇士 AC
先统计出每次施法最长持续时间,然后二分求得最小的时间使得满足最小伤害值
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 101;
ll n, h;
ll a[N];
bool check(ll x) {
ll res = 0;
for (int i = 0; i < n; i ++) {
res += min(a[i], x);
if (res >= h) return true;
}
return false;
}
void solve() {
cin >> n >> h;
for (int i = 0; i < n; i ++) cin >> a[i];
for (int i = 0; i < n - 1; i ++) {
a[i] = a[i + 1] - a[i];
}
a[n - 1] = 1e18;
ll l = 0, r = 1e18;
while (l < r) {
ll mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
}
int main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int T; cin >> T;
while (T --) {
solve();
}
return 0;
}