做题记录 (2024-11-21 to 2024-11-29)
CF1922F Replace on Segment *2500,区间DP
可证最优解可以不存在相交且不包含的操作区间,
设
第一个转移方程看上去可能会发生自转移,但是右边的
CF981D Bookshelves *1900,贪心,DP
显然是先按位贪心,然后 DP 求出能否达到当前答案。
CF1915G Bicycles *1800,最短路
这是一类最短路建模题,需要在状态记录上面做改动。
如果直接跑一维的 Dijkstra 是不行的,因为可以先到达一个速度较小的城市获取它的速度,然后再返回。考虑增加状态。
设
CF1714D Color with Occurrences *1600,DP
一时脑抽,不会做 *1600,真的唐完了。
设
ABC373F Knapsack with Diminishing Values *2018,背包DP
好题。
第一眼想到的是多重背包,用单调队列优化,但是发现
这种式子肯定是想办法拆贡献了。发现
朴素的想法是设
那么我们考虑如何优化状态,先把物品按照重量分组,然后设
接着需要预处理出
设
P2943 [USACO09MAR] Cleaning Up G *提高+/省选−,DP
设
但是,最终答案的其中一段的不同的数的个数不会超过
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N = 2e5 + 5;
const ll inf = 1e18;
int n, m;
int pos[N];
ll a[N], b[N], dp[N];
int pre[N], nex[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
b[i] = a[i];
}
int k = n;
sort(b + 1, b + 1 + k);
k = unique(b + 1, b + 1 + k) - (b + 1);
for (int i = 1; i <= n; i++) {
a[i] = lower_bound(b + 1, b + 1 + k, a[i]) - b;
pre[i] = i - 1;
nex[i - 1] = i;
if (pos[a[i]]) {
nex[pre[pos[a[i]]]] = nex[pos[a[i]]];
pre[nex[pos[a[i]]]] = pre[pos[a[i]]];
}
pos[a[i]] = i;
ll cnt = 1;
int now = i;
dp[i] = inf;
while (now && cnt * cnt <= n) {
now = pre[now];
dp[i] = min(dp[i], dp[now] + cnt * cnt);
cnt++;
}
}
cout << dp[n] << "\n";
return 0;
}
ABC381F 1122 Subsequence *1739,状压DP
设
设
预处理
于是
CF1030E Vasya and Good Sequences *2000,计数
一个序列异或和为
那么这些数
考虑枚举右端点,设
第一个限制条件可以用前缀和计算,对于不满足第二个限制条件,再把它减去。
由于每个数都至少会提供一个
P3959 [NOIP2017 提高组] 宝藏 *省选/NOI−,状压DP
设
答案为
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N = 12 + 5, M = 1e3 + 5, S = (1 << 12) + 5;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, m, bit[N], cnt[S];
ll G[N][N], dp[N][N][S];
void getmin(ll &x, ll y) { x = min(x, y); }
void init() {
memset(G, 0x3f, sizeof(G));
memset(dp, 0x3f, sizeof(dp));
}
int main() {
init();
cin >> n >> m;
int u, v; ll w;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
getmin(G[u][v], w);
getmin(G[v][u], w);
}
int tot = (1 << n) - 1;
bit[0] = 1;
for (int i = 1; i <= n; i++) {
bit[i] = bit[i - 1] << 1;
}
for (int i = 1; i <= tot; i++) {
cnt[i] = cnt[i - (i & -i)] + 1;
}
for (int dep = n; dep >= 1; dep--) {
for (int i = 1; i <= n; i++) {
dp[dep][i][0] = 0;
}
}
for (int dep = n - 1; dep >= 1; dep--) {
for (int cur = 1; cur <= tot; cur++) {
for (int u = 1; u <= n; u++) {
if ((cur & bit[u - 1]) || (n - cnt[cur] < dep)) continue;
for (int to = cur; to; to = (to - 1) & cur) {
for (int v = 1; v <= n; v++) {
if (!(to & bit[v - 1]) || G[u][v] == inf) continue;
getmin(dp[dep][u][cur], dp[dep + 1][v][to ^ bit[v - 1]] + dep * G[u][v] + dp[dep][u][cur - to]);
}
}
}
}
}
ll ans = inf;
for (int i = 1; i <= n; i++) {
getmin(ans, dp[1][i][tot ^ bit[i - 1]]);
}
cout << ans << '\n';
return 0;
}
P3953 [NOIP2017 提高组] 逛公园 *省选/NOI−,图上DP
设
转移有,
答案,
要按照
但是这样处理不了
转移先枚举第二维,然后按照排序顺序,对于每个点从它的出边向外转移。
对于
-
, -
,
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using pli = pair<ll, int>;
const int N = 1e5 + 5, K = 50 + 5;
const ll inf = 1e18;
int n, m;
ll k, mod;
ll dp[N][K]; // dp[i][j] : dis[1][i] == dis1[i] + k 的路径数量
vector<pair<int, ll>> G[N], rG[N]; // 原图,反图
vector<int> G0[N]; // 0边图
int in[N], ord[N], inx; // 入度,topo序
int id[N], pos[N];
ll dis1[N], disu[N], disn[N];
bool vis[N];
void dijkstra(int st, ll *dis, vector<pair<int, ll>> *G) {
priority_queue<pli, vector<pli>, greater<pli>> pq;
fill(dis + 1, dis + 1 + n, inf);
fill(vis + 1, vis + 1 + n, false);
dis[st] = 0;
pq.emplace(0, st);
while (!pq.empty()) {
int u = pq.top().second;
pq.pop();
if (vis[u]) continue;
vis[u] = true;
for (auto e : G[u]) {
int v = e.first;
ll w = e.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
pq.emplace(dis[v], v);
}
}
}
}
void topo() {
queue<int> q;
for (int i = 1; i <= n; i++) {
if (in[i] == 0) q.push(i);
}
while (!q.empty()) {
int u = q.front();
q.pop();
ord[u] = ++inx;
for (int v : G0[u]) {
if (--in[v] == 0) q.push(v);
}
}
}
void init() {
memset(dp, 0, sizeof(dp));
memset(in, -1, sizeof(in));
inx = 0;
}
void clear(int n) {
for (int i = 1; i <= n; i++) {
G[i].clear();
rG[i].clear();
in[i] = -1;
G0[i].clear();
}
}
void Solve() {
init();
cin >> n >> m >> k >> mod;
int u, v; ll w;
for (int i = 1; i <= m; i++) {
cin >> u >> v >> w;
G[u].emplace_back(v, w);
rG[v].emplace_back(u, w); // 反图
}
// 最短路
dijkstra(1, dis1, G); // 1 到 u
dijkstra(n, disu, rG); // u 到 n
dijkstra(n, disn, G); // n 到 u
// 建0边图
for (int u = 1; u <= n; u++) {
for (auto e : G[u]) {
int v = e.first;
ll w = e.second;
if (w == 0) {
if (in[u] == -1) in[u] = 0;
if (in[v] == -1) in[v] = 0;
G0[u].push_back(v);
in[v]++;
}
}
}
// 对0边图拓扑排序
topo();
// 两种无解的情况
for (int i = 2; i < n; i++) {
if (in[i] > 0 && dis1[i] + disu[i] <= dis1[n] + k) {
cout << -1 << '\n';
clear(n);
return;
}
}
if (dis1[n] == 0 && disn[1] == 0) {
cout << -1 << '\n';
clear(n);
return;
}
// 排序
iota(id + 1, id + 1 + n, 1);
sort(id + 1, id + 1 + n, [&](int i, int j) { return dis1[i] == dis1[j] ? ord[i] < ord[j] : dis1[i] < dis1[j]; });
for (int i = 1; i <= n; i++) {
pos[id[i]] = i;
}
sort(dis1 + 1, dis1 + 1 + n);
// 转移
dp[1][0] = 1 % mod;
for (int i = 0; i <= k; i++) {
for (int j = 1; j <= n; j++) {
int u = id[j];
for (auto e : G[u]) {
int v = e.first;
ll w = e.second;
if (dis1[pos[u]] + i + w - dis1[pos[v]] <= k && dis1[pos[u]] + i + w - dis1[pos[v]] >= 0) {
dp[v][dis1[pos[u]] + i + w - dis1[pos[v]]] += dp[u][i];
dp[v][dis1[pos[u]] + i + w - dis1[pos[v]]] %= mod;
}
}
}
}
ll ans = 0;
for (int i = 0; i <= k; i++) ans = (ans + dp[n][i]) % mod;
cout << ans << '\n';
clear(n);
}
int main() {
int T;
cin >> T;
while (T--) Solve();
return 0;
}
P3960 [NOIP2017 提高组] 列队 *省选/NOI−,线段树
考虑用
维护其区间和,有元素的地方为
对于删除操作
由于
点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
const int N = 3e5 + 5;
int n, m, q;
struct SegmentTree {
int tot, ls[N * 20], rs[N * 20], sum[N * 20];
ll id[N * 20];
ll query(int root, int &x, int l, int r, int k) {
if (!x) {
x = ++tot;
if (root == n + 1) {
if (r <= n) sum[x] = r - l + 1;
else if (l <= n) sum[x] = n - l + 1;
else sum[x] = 0;
} else {
if (r <= m - 1) sum[x] = r - l + 1;
else if (l <= m - 1) sum[x] = m - l;
else sum[x] = 0;
}
if (l == r) {
if (root == n + 1) id[x] = 1ll * l * m;
else id[x] = 1ll * (root - 1) * m + l;
}
}
sum[x]--;
if (l == r) return id[x];
int mid = l + r >> 1;
if ((!ls[x] && k <= mid - l + 1) || k <= sum[ls[x]]) return query(root, ls[x], l, mid, k);
else {
if (!ls[x]) k -= mid - l + 1;
else k -= sum[ls[x]];
return query(root, rs[x], mid + 1, r, k);
}
}
void update(int root, int &x, int l, int r, int k, ll v) {
if (!x) {
x = ++tot;
if (root == n + 1) {
if (r <= n) sum[x] = r - l + 1;
else if (l <= n) sum[x] = n - l + 1;
else sum[x] = 0;
} else {
if (r <= m - 1) sum[x] = r - l + 1;
else if (l <= m - 1) sum[x] = m - l;
else sum[x] = 0;
}
}
sum[x]++;
if (l == r) return void(id[x] = v);
int mid = l + r >> 1;
if (k <= mid) update(root, ls[x], l, mid, k, v);
else update(root, rs[x], mid + 1, r, k, v);
}
} sgt;
int root[N], len[N];
int main() {
cin >> n >> m >> q;
for (int i = 1; i <= n; i++) {
len[i] = m - 1;
}
len[n + 1] = n;
int _n = max(n, m) + q;
int x, y; ll id;
while (q--) {
cin >> x >> y;
if (y == m) {
id = sgt.query(n + 1, root[n + 1], 1, _n, x);
sgt.update(n + 1, root[n + 1], 1, _n, ++len[n + 1], id);
} else {
id = sgt.query(x, root[x], 1, _n, y);
sgt.update(x, root[x], 1, _n, ++len[x], sgt.query(n + 1, root[n + 1], 1, _n, x));
sgt.update(n + 1, root[n + 1], 1, _n, ++len[n + 1], id);
}
cout << id << '\n';
}
return 0;
}
CF2020D Connect the Dots *1800,并查集,根号分治
因为
推广到
CF1913D Array Collapse *2100,计数DP
设
设
转移:
答案是
本文作者:chenwenmo
本文链接:https://www.cnblogs.com/chenwenmo/p/18561836
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步