10.3 总结
T1
每一个数列有 \(m\) 种变式,而总共有 \(m^n\) 个数列,所以答案是 \(m^{n-1}\),赛事 AC 了。
#include <fstream>
using namespace std;
using ll = long long;
const ll kMod = 1e9 + 7;
ifstream cin("sum.in");
ofstream cout("sum.out");
ll t, n, m;
ll fpow(ll a, ll b) {
ll res = 1;
for (; b; res = b & 1 ? res * a % kMod : res, b >>= 1, a = a * a % kMod) {
}
return res % kMod;
}
int main() {
for (cin >> t; t; t--) {
cin >> n >> m;
cout << fpow(m, n - 1) << '\n';
}
return 0;
}
T2
一道 DP 题,终点是如何对 dp 数组转移。
具体来讲,枚举可能的运输时间,再运输时间的限制下进行背包DP,时间复杂度 \(O(b^2(\sum a_i)^2)\)。
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <fstream>
using namespace std;
const int kMaxN = 50;
ifstream cin("fruit.in");
ofstream cout("fruit.out");
long long n, a[kMaxN], b[3], c[3][kMaxN], d[3][kMaxN], u[3][kMaxN], f[kMaxN][kMaxN][kMaxN][kMaxN][2], ans = 3e15;
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
cin >> b[1] >> b[2];
for (int i = 1; i <= n; i++) {
cin >> c[1][i];
}
for (int i = 1; i <= n; i++) {
cin >> c[2][i];
}
for (int i = 1; i <= n; i++) {
cin >> d[1][i];
}
for (int i = 1; i <= n; i++) {
cin >> d[2][i];
}
for (int i = 1; i <= n; i++) {
cin >> u[1][i];
}
for (int i = 1; i <= n; i++) {
cin >> u[2][i];
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= a[i]; j++) {
for (int k = 0; k <= b[1]; k++) {
for (int l = 0; l <= b[2]; l++) {
f[i][j][k][l][0] = f[i][j][k][l][1] = 3e15;
}
}
}
}
f[0][0][0][0][0] = f[0][0][0][0][1] = 0;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= a[i]; j++) {
for (int k = 0; k <= b[1]; k++) {
for (int l = 0; l <= b[2]; l++) {
if (a[i + 1] == 0) {
f[i + 1][j][k][l][0] = f[i][j][k][l][0];
f[i + 1][j][k][l][1] = f[i][j][k][l][1];
continue;
}
for (int p = 0; p <= a[i + 1]; p++) {
long long tmp = max(f[i][j][k][l][0], max(d[2][i + 1] * (a[i + 1] - p), d[1][i + 1] * p)) + max(f[i][j][k][l][1], max(u[2][i + 1] * (a[i + 1] - p), u[1][i + 1] * p));
if (p == 0) {
if (tmp < f[i + 1][p][k][l + c[2][i + 1]][0] + f[i + 1][p][k][l + c[2][i + 1]][1]) {
f[i + 1][p][k][l + c[2][i + 1]][0] = max(f[i][j][k][l][0], max(d[2][i + 1] * (a[i + 1] - p), d[1][i + 1] * p));
f[i + 1][p][k][l + c[2][i + 1]][1] = max(f[i][j][k][l][1], max(u[2][i + 1] * (a[i + 1] - p), u[1][i + 1] * p));
}
} else if (p == a[i + 1]) {
if (tmp < f[i + 1][p][k + c[1][i + 1]][l][0] + f[i + 1][p][k + c[1][i + 1]][l][1]) {
f[i + 1][p][k + c[1][i + 1]][l][0] = max(f[i][j][k][l][0], max(d[2][i + 1] * (a[i + 1] - p), d[1][i + 1] * p));
f[i + 1][p][k + c[1][i + 1]][l][1] = max(f[i][j][k][l][1], max(u[2][i + 1] * (a[i + 1] - p), u[1][i + 1] * p));
}
} else {
if (tmp < f[i + 1][p][k + c[1][i + 1]][l + c[2][i + 1]][0] + f[i + 1][p][k + c[1][i + 1]][l + c[2][i + 1]][1]) {
f[i + 1][p][k + c[1][i + 1]][l + c[2][i + 1]][0] = max(f[i][j][k][l][0], max(d[2][i + 1] * (a[i + 1] - p), d[1][i + 1] * p));
f[i + 1][p][k + c[1][i + 1]][l + c[2][i + 1]][1] = max(f[i][j][k][l][1], max(u[2][i + 1] * (a[i + 1] - p), u[1][i + 1] * p));
}
}
}
}
}
}
}
for (int j = 0; j <= a[n]; j++) {
for (int k = 0; k <= b[1]; k++) {
for (int l = 0; l <= b[2]; l++) {
ans = min(ans, f[n][j][k][l][0] + f[n][j][k][l][1]);
}
}
}
cout << ans;
return 0;
}
T3
一道用 set 来模拟的题目,两个 set 一个用来存位置排序,一个用来存长度排序,然后乱搞即可。
有一个人进入的时候可以在第二个set里面查询,然后把左右两端处理一下,有人出来了也是一样的,把两边的空段连接起来就行了
#include <fstream>
#include <set>
#include <vector>
using namespace std;
const int kMaxN = 4e5 + 1;
ifstream cin("location.in");
ofstream cout("location.out");
int n, m, pos[kMaxN];
struct Seg {
int b, e;
Seg() {}
Seg(int b, int e) : b(b), e(e) {}
int Dis() const {
return (e - b) / (b == 1 || e == n ? 1 : 2);
}
int Choose() {
return (b == 1 ? 1 : (e == n ? n : (e + b) / 2));
}
};
struct Cmp1 {
bool operator()(const Seg& a, const Seg& b) const {
return a.Dis() != b.Dis() ? a.Dis() > b.Dis() : a.b < b.b;
}
};
struct Cmp2 {
bool operator()(const Seg& a, const Seg& b) const {
return a.b < b.b;
}
};
set<Seg, Cmp1> s1;
set<Seg, Cmp2> s2;
void E(const Seg& s) {
s1.erase(s), s2.erase(s);
}
void I(const Seg& s) {
s1.insert(s), s2.insert(s);
}
int I(int o) {
Seg t = *s1.begin();
int c = t.Choose();
E(t);
if (t.b < c) {
I(Seg(t.b, c - 1));
}
if (t.e > c) {
I(Seg(c + 1, t.e));
}
return c;
}
void O(int x) {
vector<Seg> o;
if (x != 1) {
Seg t = *(--s2.lower_bound(Seg(x, -1)));
if (t.e == x - 1) {
o.push_back(t);
}
}
if (x != n && s2.lower_bound(Seg(x, -1)) != s2.end()) {
Seg t = *s2.lower_bound(Seg(x, -1));
if (t.b == x + 1) {
o.push_back(t);
}
}
Seg t;
if (o.size() == 2) {
t = Seg(o[0].b, o[1].e);
} else if (o.size() == 1) {
if (o[0].b == x + 1) {
t = Seg(x, o[0].e);
} else {
t = Seg(o[0].b, x);
}
} else {
t = Seg(x, x);
}
for (Seg i : o) {
E(i);
}
I(t);
}
int main() {
cin >> n >> m;
I(Seg(1, n));
for (int p = 1, t; p <= 2 * m; p++) {
cin >> t;
if (pos[t]) {
O(pos[t]);
} else {
pos[t] = I(t);
}
}
for (int i = 1; i <= m; i++) {
cout << pos[i] << '\n';
}
return 0;
}
T4
一道 Kruskal加上LCA的题目,超级缝合怪,出题人还卡long long ,所以要用 int128。
具体实现就是先用 Kruskal 来把图改成树,然后使用LCA来求出每两个点之间的点数量和距离就行了。
#include <algorithm>
#include <fstream>
#include <vector>
using namespace std;
using ll = long long;
using i128 = __int128;
const ll kMaxN = 2e5 + 1;
ifstream cin("run.in");
ofstream cout("run.out");
int n, m, k, t0, ans, fat[kMaxN];
struct Side {
ll u, v, w;
} s[kMaxN];
struct Node {
int f[20], d;
i128 s;
vector<pair<int, ll>> e;
} v[kMaxN];
int Find(int x) {
return fat[x] = fat[x] == x ? x : Find(fat[x]);
}
void Walk(int x, int fa) {
v[x].d = v[fa].d + 1, v[x].f[0] = fa;
for (auto i : v[x].e) {
ll y = i.first, z = i.second;
if (y != fa) {
v[y].s = v[x].s + z;
Walk(y, x);
}
}
}
void CalcF() {
for (ll j = 1; j < 20; j++) {
for (ll i = 1; i <= n; i++) {
v[i].f[j] = v[v[i].f[j - 1]].f[j - 1];
}
}
}
ll LCA(ll x, ll y) {
if (v[x].d < v[y].d) {
swap(x, y);
}
for (ll i = 19; i >= 0; i--) {
if (v[v[x].f[i]].d >= v[y].d) {
x = v[x].f[i];
}
}
if (x == y) {
return x;
}
for (ll i = 19; i >= 0; i--) {
if (v[x].f[i] != v[y].f[i]) {
x = v[x].f[i], y = v[y].f[i];
}
}
return v[x].f[0];
}
ll Len(ll x, ll y) {
return v[x].d + v[y].d - 2 * v[LCA(x, y)].d - 1;
}
i128 Dis(ll x, ll y) {
return v[x].s + v[y].s - 2 * v[LCA(x, y)].s;
}
void print(i128 x) {
if (x >= 10) {
print(x / 10);
}
cout.put(x % 10 + '0');
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
fat[i] = i;
}
for (int i = 1; i <= m; i++) {
cin >> s[i].u >> s[i].v >> s[i].w;
}
// 最小生成树 begin
sort(s + 1, s + m + 1, [](Side a, Side b) { return a.w < b.w; });
for (int i = 1; i <= m; i++) {
int x = s[i].u, y = s[i].v, w = s[i].w;
int fx = Find(x), fy = Find(y);
if (fx != fy) {
fat[fy] = fx;
v[x].e.push_back({y, w});
v[y].e.push_back({x, w});
}
}
// 最小生成树 end
// LCA begin
Walk(1, 0), CalcF();
// LCA end
// 回答 begin
cin >> k >> t0;
ll lst = 0;
i128 ans = 0, cnt = 0;
for (ll i = 1, x; i <= k; i++) {
cin >> x;
if (i != 1) {
cnt += Len(lst, x);
ans += Dis(lst, x);
}
cnt += (i != 1 && i != k);
lst = x;
}
ans += cnt * t0;
print(ans);
return 0;
}

浙公网安备 33010602011771号