花惑う夏を待つ僕に差す月明かり
6 月 25 日 ~ 7 月 4 日
其实只准备考三科。
其实学点文化课挺好的。
LG P9111 [福建省队集训2019] 最大权独立集问题
给定一颗
- 令
,其中 为点 当前的权值。 - 对于所有与
直接相连的点 ,令 。 - 删除点
。
求
考虑把贡献拆开,即计算每个点对答案的贡献。
设删除点
考虑树形 DP。我们发现,对于当前 DP 的点
注意到,本题的时间复杂度允许我们在背包之外再乘一个
- 方向为
: 对 没有额外的贡献,枚举 可达的点数 ,有 。 - 方向为
: 对 有额外 的贡献,枚举 可达的点数 ,有 。
转移完之后我们需要补上
总时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
constexpr int N = 4e2 + 5;
int n, a[N], c[N], sz[N];
LL f[N][N][N], g[N][N][N];
vector <int> e[N];
void dfs(int u) {
sz[u] = 1;
for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) f[u][i][k] = -1e18;
for (int k = 1; k <= n; k++) f[u][1][k] = 1LL * a[u] * k;
for (auto v : e[u]) {
dfs(v);
static LL tmp[N][N];
for (int k = 1; k <= n; k++) for (int i = 1; i <= sz[u]; i++) tmp[i][k] = f[u][i][k], f[u][i][k] = -1e18;
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= sz[u]; i++) {
for (int j = 1; j <= sz[v]; j++) {
f[u][i + j][k] = max(f[u][i + j][k], tmp[i][k] + f[v][j][j]);
}
LL val = -1e18;
for (int j = 1; j <= min(sz[v], n - k); j++) val = max(val, f[v][j][j + k]);
f[u][i][k] = max(f[u][i][k], tmp[i][k] + val);
}
}
sz[u] += sz[v];
}
}
signed main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 2; i <= n; i++) {
cin >> c[i];
e[c[i]].push_back(i);
}
dfs(1);
LL ans = -1e18;
for (int i = 1; i <= n; i++) ans = max(ans, f[1][i][i]);
cout << ans << "\n";
return 0;
}
LG P7740 [NOI2021] 机器人游戏
有 0
、1
或空格子。第
-
如果它所在序列全是空格子,则不会执行后面的任何操作。
-
否则它会按顺序执行操作序列:
R
表示往右走一步,如果走出去了就爆炸;0
或1
表示把脚下格子改成0
或1
;*
表示把当前格子异或1
。对于修改操作,如果脚下是空格子则不会产生任何影响。
对于一个初始序列状态、最终序列状态的二元组
求合法方案总数对
考虑容斥,先转化成求不合法的方案数,接着转化为计算钦定若干个位置是起点,剩下随便的方案数。当然样例解释已经教会了我们该怎么容斥:直接枚举钦定的起点集合
考虑怎么计算这个方案数。容易发现,在一个机器人操作完之后,对于初始状态某个位置上的值
分类讨论:
- 如果限制中既有
又有 ,或者既有 又有 :显然唯一可能的合法情况就是该位置为空,方案数为 。 - 否则,如果限制中有
或 ,且有 或 :那么要么格子为空,否则输入输出就确定了,方案数为 。 - 对于剩下的情况,每一种输入恰对应一种输出,加上空格子的情况,方案数为
。
这样每个位置就是独立的了。特判一下机器人爆炸的情况,直接计算的时间复杂度为
注意到 R
,那么对于当前的
然后我们对于前
我们发现瓶颈无非是对于一个给定的起点集合,在所有序列中求出对某个位置
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef vector <int> vi;
constexpr int N = 35, M = 1e3 + 5, mod = 1e9 + 7;
int n, m, L[M], oper[M][N], c[M], inall;
char str[M][N * 3];
int f[N][2][1 << 17], pw2[M], pw3[M];
bitset < M > all, rc[N][4], pc[N], Z[1 << 17][4];
vi st[N];
signed main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= m; i++) cin >> (str[i] + 1), L[i] = strlen(str[i] + 1);
pw2[0] = pw3[0] = 1;
for (int i = 1; i <= m; i++) pw2[i] = 1LL * pw2[i - 1] * 2 % mod, pw3[i] = 1LL * pw3[i - 1] * 3 % mod;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= L[i]; j++) {
if (str[i][j] == 'R') ++c[i], oper[i][c[i]] = 0;
if (str[i][j] == '0') oper[i][c[i]] = 2;
if (str[i][j] == '1') oper[i][c[i]] = 3;
if (str[i][j] == '*') oper[i][c[i]] ^= 1;
}
st[c[i]].push_back(i);
}
for (int i = 1; i <= m; i++) all.set(i);
int ans = 1;
for (int i = 1; i <= n * m; i++) ans = 1LL * ans * 3 % mod;
for (int r = n; r >= 0; r--) {
for (auto t : st[n - r]) {
for (int i = 0; i <= n + 2; i++) rc[i][oper[t][i]].set(t);
for (int i = c[t] + 1; i <= n + 2; i++) pc[i].set(t);
++inall;
}
f[0][0][0] = 1;
for (int i = 1; i <= r; i++) {
int up = 1 << min(n - r + 1, i - 1), xup = 1 << min(n - r + 1, i), msk = xup - 1;
for (int s = 0; s <= xup - 1; s++) f[i][0][s] = f[i][1][s] = 0;
for (int z = 0; z <= 1; z++) {
for (int s = 0; s <= up - 1; s++) {
int _z = z | ((s << 1) > msk);
if (i < r) f[i][_z][(s << 1) & msk] = (f[i][_z][(s << 1) & msk] + f[i - 1][z][s]) % mod;
f[i][_z][(s << 1 | 1) & msk] = (f[i][_z][(s << 1 | 1) & msk] + mod - f[i - 1][z][s]) % mod;
}
}
for (int z = 0; z <= 1; z++) {
int _z = z | (i < r);
for (int j = 0; j <= min(n - r + 1, i) - 1; j++) {
for (int k = _z; k <= 3; k++) Z[1 << j][k] = rc[j][k];
if (_z == 0) Z[1 << j][0] |= pc[j];
}
for (int s = 0; s <= xup - 1; s++) {
if (s) for (int k = _z; k <= 3; k++) Z[s][k] = Z[s & -s][k] | Z[s ^ (s & -s)][k];
if (_z) {
bitset < M > v1 = Z[s][1] | (Z[s][2] & Z[s][3]), v2 = (Z[s][2] | Z[s][3]) & (all ^ v1);
int px = v1.count(), py = v2.count();
f[i][z][s] = 1LL * f[i][z][s] * pw2[py] % mod * pw3[inall - px - py] % mod;
} else {
bitset < M > v1 = (Z[s][0] & Z[s][1]) | (Z[s][2] & Z[s][3]), v2 = (Z[s][0] | Z[s][1]) & (Z[s][2] | Z[s][3]) & (all ^ v1);
int px = v1.count(), py = v2.count();
f[i][z][s] = 1LL * f[i][z][s] * pw2[py] % mod * pw3[inall - px - py] % mod;
}
}
}
}
int dis = min(n - r + 1, r), xup = 1 << dis;
for (int z = 0; z <= 1; z++) {
for (int d = 1; d <= n - r; d++) {
for (int j = 0; j <= dis - 1; j++) for (int k = z; k <= 3; k++) Z[1 << j][k] = rc[j + d][k];
for (int s = 0; s <= xup - 1; s++) {
if (s) for (int k = z; k <= 3; k++) Z[s][k] = Z[s & -s][k] | Z[s ^ (s & -s)][k];
if (z) {
bitset < M > v1 = Z[s][1] | (Z[s][2] & Z[s][3]), v2 = (Z[s][2] | Z[s][3]) & (all ^ v1);
int px = v1.count(), py = v2.count();
f[r][z][s] = 1LL * f[r][z][s] * pw2[py] % mod * pw3[inall - px - py] % mod;
} else {
bitset < M > v1 = (Z[s][0] & Z[s][1]) | (Z[s][2] & Z[s][3]), v2 = (Z[s][0] | Z[s][1]) & (Z[s][2] | Z[s][3]) & (all ^ v1);
int px = v1.count(), py = v2.count();
f[r][z][s] = 1LL * f[r][z][s] * pw2[py] % mod * pw3[inall - px - py] % mod;
}
}
}
}
for (int z = 0; z <= 1; z++) {
for (int s = 0; s <= xup - 1; s++) {
ans = (ans + mod - f[r][z][s]) % mod;
}
}
}
cout << ans << "\n";
return 0;
}
LG P6072『MdOI R1』Path
给定一棵
一个简单的转化:我们以
求
但求
进一步发现求
总时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> pi;
constexpr int N = 3e4 + 5;
int n, a[N], p[N], q[N], ans; // p : in subtree, q : out of subtree
vector <pi> e[N];
int par[N], key[N];
struct Trie_with_position {
int ch[N << 6][2], pos[N << 6];
int tot = 1;
int ns; pi ans;
void ins(int x, int p) {
int u = 1, ret = 0;
for (int i = 30; i >= 0; i--) {
int c = (x >> i) & 1;
if (ch[u][c ^ 1]) ret |= (1 << i), c ^= 1;
u = ch[u][c];
}
if (ret > ns) ns = ret, ans = make_pair(pos[u], p);
u = 1;
for (int i = 30; i >= 0; i--) {
int c = (x >> i) & 1;
if (!ch[u][c]) ch[u][c] = ++tot;
u = ch[u][c];
}
pos[u] = p;
}
void erase(int x) {
pos[x] = 0;
if (ch[x][0]) erase(ch[x][0]), ch[x][0] = 0;
if (ch[x][1]) erase(ch[x][1]), ch[x][1] = 0;
}
void clr() {
tot = 1, ns = 0, erase(1);
}
} all, tr;
void dfs0(int u, int ff) {
for (auto [v, w] : e[u]) if (v != ff) {
a[v] = a[u] ^ w, par[v] = u;
dfs0(v, u);
}
}
void add_tree(int u, int ff) {
tr.ins(a[u], u);
for (auto [v, w] : e[u]) if (v != ff) add_tree(v, u);
}
void calc_q(int x) {
tr.clr();
static int seq[N], c;
c = 0;
while (x > 0) seq[++c] = x, key[x] = 1, x = par[x];
if (c == 1) return;
reverse(seq + 1, seq + c + 1);
tr.ins(a[1], 1);
for (int i = 2; i <= c; i++) {
int u = seq[i - 1];
for (auto [v, w] : e[u]) if (key[v] == 0) add_tree(v, u);
q[seq[i]] = tr.ns;
tr.ins(a[seq[i]], seq[i]);
}
for (int i = 1; i <= c; i++) key[seq[i]] = 0;
}
void calc_p(int x) {
tr.clr();
static int seq[N], c;
c = 0;
while (x > 0) seq[++c] = x, key[x] = 1, x = par[x];
if (c == 1) return;
add_tree(seq[1], seq[2]);
p[seq[1]] = tr.ns;
for (int i = 2; i <= c; i++) {
int u = seq[i];
for (auto [v, w] : e[u]) if (key[v] == 0) add_tree(v, u);
tr.ins(a[u], u);
p[u] = tr.ns;
}
for (int i = 1; i <= c; i++) key[seq[i]] = 0;
}
signed main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1, x, y, z; i < n; i++) {
cin >> x >> y >> z;
e[x].emplace_back(y, z);
e[y].emplace_back(x, z);
}
dfs0(1, 0);
for (int i = 1; i <= n; i++) all.ins(a[i], i);
int tx, ty;
tie(tx, ty) = all.ans;
calc_q(tx);
calc_q(ty);
int _tx = tx, _ty = ty;
while (_tx > 0) key[_tx] = 1, _tx = par[_tx];
while (_ty > 0) key[_ty] = 1, _ty = par[_ty];
int seq[N], c = 0;
for (int i = 1; i <= n; i++) if (key[i] == 0) {
q[i] = tr.ns;
bool ok = false;
for (auto [v, w] : e[i]) if (key[v] == 1) {
ok = true; break;
}
if (ok == true) seq[++c] = i;
}
for (int i = 1; i <= c; i++) {
tr.clr();
add_tree(seq[i], par[seq[i]]);
p[seq[i]] = tr.ns;
}
for (int i = 1; i <= n; i++) key[i] = 0;
calc_p(tx);
calc_p(ty);
for (int i = 2; i <= n; i++) ans = max(ans, p[i] + q[i]);
cout << ans << "\n";
return 0;
}
LOJ #3968. 「JOISC 2023 Day1」护照
有
关键的性质是,由于每次拓展的是包含
不过这好像也没有想象中那么好做,不妨再考虑一个弱化:如果只要求能到达
我们发现,要求某个点能够同时到达
总时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> pi;
constexpr int N = 2e5 + 5;
int n, q, L[N], R[N];
vector <pi> e[N << 2]; int id[N << 2], tot, a[N << 2], b[N << 2], d[N << 2];
#define m ((l + r) >> 1)
void build(int x, int l, int r) {
id[x] = ++tot;
if (l == r) return e[l].emplace_back(id[x], 0), void();
build(x << 1, l, m), build(x << 1 | 1, m + 1, r);
e[id[x << 1]].emplace_back(id[x], 0);
e[id[x << 1 | 1]].emplace_back(id[x], 0);
}
void add(int x, int l, int r, int ql, int qr, int v) {
if (ql <= l && qr >= r) return e[id[x]].emplace_back(n + v, 0), void();
if (ql <= m) add(x << 1, l, m, ql, qr, v);
if (qr > m) add(x << 1 | 1, m + 1, r, ql, qr, v);
}
#undef m
signed main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
tot = 2 * n;
build(1, 1, n);
for (int i = 1; i <= n; i++) {
cin >> L[i] >> R[i];
add(1, 1, n, L[i], R[i], i), e[n + i].emplace_back(i, 1);
}
for (int i = 1; i <= tot; i++) a[i] = 1e9;
a[1] = 0;
deque <int> que;
que.push_front(1);
while (!que.empty()) {
int u = que.front(); que.pop_front();
for (auto [v, w] : e[u]) {
if (a[v] > a[u] + w) {
a[v] = a[u] + w;
if (w == 0) que.push_front(v);
if (w == 1) que.push_back(v);
}
}
}
for (int i = 1; i <= tot; i++) b[i] = 1e9;
b[n] = 0;
que.push_front(n);
while (!que.empty()) {
int u = que.front(); que.pop_front();
for (auto [v, w] : e[u]) {
if (b[v] > b[u] + w) {
b[v] = b[u] + w;
if (w == 0) que.push_front(v);
if (w == 1) que.push_back(v);
}
}
}
for (int i = 1; i <= tot; i++) d[i] = 1e9;
for (int i = 1; i <= n; i++) d[i] = a[i] + b[i] - (1 < i && i < n);
int seq[N];
for (int i = 1; i <= n; i++) seq[i] = i;
sort(seq + 1, seq + n + 1, [&](int i, int j) {
return d[i] < d[j];
});
for (int i = 1; i <= n; i++) que.push_back(seq[i]);
while (!que.empty()) {
int u = que.front(); que.pop_front();
for (auto [v, w] : e[u]) {
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
if (w == 0) que.push_front(v);
if (w == 1) que.push_back(v);
}
}
}
cin >> q;
while (q--) {
int x; cin >> x;
if (d[x] > n) cout << "-1\n";
else cout << d[x] << "\n";
}
return 0;
}
LOJ #3970. 「JOISC 2023 Day2」议会
有
- 抽签选一个主席。
- 在剩下的人里抽签选一个副主席。
- 在剩下的人中,如果有
名议员同意某项提案,那么该提案就会被通过。
对于每个人,求出:若其被选为主席,能够通过的最大提案数。
考虑当主席确定的时候该怎么做:容易发现此时副主席选谁只会影响得票数恰好是
具体来说,我们先高维后缀和求出可能最优的,满足
总时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> pi;
constexpr int N = 3e5 + 5, M = 21;
int n, m, a[N], g1[1 << M], g2[1 << M], c[M];
signed main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1, x; j <= m; j++) {
cin >> x;
if (x) ++c[m - j];
a[i] = (a[i] << 1) | (x ^ 1);
}
if (g1[a[i]]) g2[a[i]] = i;
else g1[a[i]] = i;
}
for (int s = 1 << m; s >= 0; s--) {
for (int i = 0; i < m; i++) if (!((s >> i) & 1)) { int t = s | (1 << i);
if (!g1[s]) {
g1[s] = g1[t], g2[s] = g2[t];
} else {
if (g1[t]) {
if (g1[s] != g1[t]) g2[s] = g1[s], g1[s] = g1[t];
}
if (g2[t]) {
if (g2[s] != g2[t]) g2[s] = g2[t];
}
}
}
}
for (int s = 0; s < 1 << m; s++) {
for (int i = 0; i < m; i++) if ((s >> i) & 1) { int t = s ^ (1 << i);
vector <int> v;
v.push_back(g1[s]);
v.push_back(g2[s]);
v.push_back(g1[t]);
v.push_back(g2[t]);
sort(v.begin(), v.end(), [&](int i, int j) {
return __builtin_popcount(a[i] & s) > __builtin_popcount(a[j] & s);
});
v.erase(unique(v.begin(), v.end()), v.end());
g1[s] = v[0];
if (v.size() > 1) g2[s] = v[1];
}
}
for (int i = 1; i <= n; i++) {
int ret = 0, s = 0;
for (int j = 0; j < m; j++) {
int nc = c[j];
if (!((a[i] >> j) & 1)) nc--;
if (nc > n / 2) ret++;
if (nc == n / 2) s |= 1 << j;
}
if (g1[s] == i) {
ret += __builtin_popcount(a[g2[s]] & s);
} else {
ret += __builtin_popcount(a[g1[s]] & s);
}
cout << ret << "\n";
}
return 0;
}
LOJ #3972. 「JOISC 2023 Day3」合唱
给定一个长为 A
和 B
的字符串 A
和 B
的数量相等,且所有 A
在所有 B
之前。每次操作可以交换相邻两个字符,求至少需要多少次操作才能使得
感觉完全做不来这种题.
首先发现
考虑一个神秘观察:把 A
表示向右一步,B
表示向上一步,显然一次操作只会是把一个上右改成右上。当然有解的一个必要条件是它必须在对角线下方,我们把在对角线上方的部分改到对角线下方,代价提前计算掉,现在只考虑路径在对角线之下的情况。
考虑在路径上做上面那个贪心,相当于每次沿着路径往右走,碰到一个向上就一直向上直到碰到对角线再向右,两次拐弯衔接的地方可能会平移一部分路径,但这个是合法的。
于是要求
设
让我们暂时忘掉决策单调性。考虑给
总时间复杂度
code
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> pi;
typedef long long LL;
constexpr int N = 2e6 + 5;
constexpr LL inf = 1e18;
int n, k, t[N], g[N]; LL cnt[N], sum[N], pre[N], f[N], ans, ns;
int q[N], hd, tl;
LL X(int i) { return i; }
LL Y(int i) {
return f[i] + pre[i - 1] + i;
}
bool chk(LL m) {
fill(f + 1, f + n + 2, inf);
fill(g + 1, g + n + 2, k + 1);
f[1] = g[1] = 0;
q[hd = tl = 1] = 1;
for (int i = 2; i <= n + 1; i++) {
while (hd < tl && i * (X(q[hd + 1]) - X(q[hd])) > (Y(q[hd + 1]) - Y(q[hd]))) hd++;
LL val = -X(q[hd]) * i + Y(q[hd]);
f[i] = val + (cnt[i - 1] + 1) * (i - 1) - sum[i - 1] - m, g[i] = g[q[hd]] + 1;
while (hd < tl && (Y(i) - Y(q[tl])) * (X(q[tl]) - X(q[tl - 1])) < (Y(q[tl]) - Y(q[tl - 1])) * (X(i) - X(q[tl]))) tl--;
q[++tl] = i;
}
ans = f[n + 1];
return g[n + 1] <= k;
}
signed main() {
ios :: sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> k;
string str;
cin >> str;
str = ' ' + str;
int cA = 0, cB = 0;
for (int i = 1; i <= 2 * n; i++) {
if (str[i] == 'A') ++cA;
else ++cB, t[cB] = cA;
}
for (int i = 1; i <= n; i++) if (t[i] < i) ns += i - t[i], t[i] = i;
for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + t[i];
for (int i = 1; i <= n; i++) ++cnt[t[i]], sum[t[i]] += t[i];
for (int i = 1; i <= n; i++) cnt[i] += cnt[i - 1], sum[i] += sum[i - 1];
LL l = -5e11, r = 0, p = 0;
while (l <= r) {
LL m = (l + r) >> 1;
if (chk(m)) p = m, l = m + 1;
else r = m - 1;
}
chk(p);
cout << ns + ans + 1LL * p * k << "\n";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】