离烦来了(25.3)

2025/3/17 *1

2025/3/18 *2

xyk 你上来读一下题......
10 min 后:我来确定一下要报 APIO 的名单,这个叉歪kei是谁啊?

P4823

定义 \(f_i\) 表示跑出去 \(i\) 个人最大还能凑出多高。

然后比较显然的排序不等式,是简单的叠罗汉模型,也是经典的。

跟有道 abc 的 f 很类似,就是 CSP 之前给 ycz 和 hsr 看的那道,由于只是确定了顺序而不知道要选哪些所以要 dp。

转移就类似背包:

\[f_j=\max\limits_{0\leq j \leq i \, and \, f_{j-1}+b_i\geq h}(f_{j-1}-a_i) \]

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for(int i = j; i <= k; ++i)
#define pre(i, j, k) for(int i = j; i >= k; --i)

using namespace std;
using LL = long long;

const int N = 1e6 + 5;
const int M = 31;
LL n, m, s, a[N], w[N], v[N], f[N], g[N], ret;

struct node {
	int a, b;
	bool operator < (const node &A) const {return a + b < A.a + A.b;}
} t[N];

void init() {
	
}

/*
f[i] 表示跑出去 i 个人最大还能凑出多少高度
先排序不等式搞一搞然后直接转移,其中排序不等式是经典的叠罗汉类型
*/

void sol() {
	cin >> n;
	memset(f, 0xcf, sizeof f), f[0] = 0;
	rep(i, 1, n) cin >> t[i].a >> t[i].b, s += t[i].a;
	sort(t + 1, t + 1 + n);
	cin >> m, f[0] = s;
	rep(i, 1, n) {
		pre(j, i, 0)
			if(f[j - 1] + t[i].b >= m) f[j] = max(f[j], f[j - 1] - t[i].a);
	}
	pre(i, n, 0) if(f[i] >= 0) cout << i, exit(0);
}

signed main() {
#ifdef Xdik

#else
//	freopen("")
#endif
	FASTIO;
	int _; _ = 1;
	while(_--) sol();
	return 0;
}

P5365

看到这个 \(n\) 的表达式是 \(\max(5,(\log_2^n)^4)\),其实 \(n<100\)

并且注意到值域不是很大可以对值域做背包,比较简单。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for(int i = j; i <= k; ++i)
#define pre(i, j, k) for(int i = j; i >= k; --i)

using namespace std;
using LL = long long;

const int N = 1e6 + 5;
const int M = 31;
LL n, m, s, a[N], v[N], f[N], g[N], ret;

void init() {

}

void sol() {
	cin >> n >> m, f[0] = 1;
	rep(i, 1, n) cin >> a[i];
	rep(i, 1, n) cin >> v[i], s += a[i] * v[i];
	rep(i, 1, n) {
		pre(j, s, 0) {
			for(int k = 1; k <= a[i] && k * v[i] <= j; ++k) f[j] = max(f[j], f[j - k * v[i]] * k);
		}
	}
	while(f[ret] < m) ++ret;
	cout << ret;
}

signed main() {
#ifdef Xdik

#else
//	freopen("")
#endif
	FASTIO;
	int _; _ = 1;
	while(_--) sol();
	return 0;
}

P2224

奇怪的题目第一次见,不会做,看题解,得知定义状态 \(f_{i,j}\) 表示前 \(i\) 个物品机器 A 用了 \(j\) 时间机器 B 工作的最短时间

这个诡异的状态相当于是将答案加入了状态,答案就是 \(\min\limits_{0\leq i \leq \operatorname{maxtime}}\max(f_{n,i},i)\)

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for(int i = j; i <= k; ++i)
#define pre(i, j, k) for(int i = j; i >= k; --i)

using namespace std;
using LL = long long;

const int N = 3e4 + 5;
const int M = 31;
LL n, m, s, a[N], w[N], v[N], f[2][N], g[N], ret, inf = 1e14;

struct node {
	int a, b, c;
	bool operator < (const node &A) const {return a + b < A.a + A.b;}
} t[N];

void init() {
	
}

/*
f[i][j] 表示前 i 个物品机器 A 用了 j 时间机器 B 所需的最小时间,也即把其中一个答案加入状态
*/

void sol() {
	cin >> n; ret = inf;
	rep(i, 1, n) cin >> t[i].a >> t[i].b >> t[i].c;
	memset(f, 0x3f, sizeof f), f[0][0] = 0;
	rep(i, 1, n) {
		rep(j, 0, N - 5) {
			f[i & 1][j] = inf;
			if(t[i].a && j - t[i].a >= 0) f[i & 1][j] = min(f[i & 1][j], f[i - 1 & 1][j - t[i].a]);
			if(t[i].b && j >= 0) f[i & 1][j] = min(f[i & 1][j], f[i - 1 & 1][j] + t[i].b);
			if(t[i].c && j - t[i].c >= 0) f[i & 1][j] = min(f[i & 1][j], f[i - 1 & 1][j - t[i].c] + t[i].c);
		}
	}
	rep(i, 0, N - 5) ret = min(ret, max(1LL * i, f[n & 1][i]));
	cout << ret;
}

signed main() {
#ifdef Xdik

#else
//	freopen("")
#endif
	FASTIO;
	int _; _ = 1;
	while(_--) sol();
	return 0;
}

2025/3/20 *3

P6879

很少见到这种题,上次见到好像是 TQ 的 dp 单元检测的奶牛题,重新看了一遍,这类题目的通性是一个傻逼可以往两个方向走然后干一些事情(贡献提前计算类区间 dp)。

常见的状态是定义 \(f_{l,r,0/1}\) 表示从 \(l\to r\) 最后停留在 \(l/r\) 对什么什么的最大最小值。

转移比较简单,考虑从 \([l+1,r]\)\([l,r-1]\) 这两个区间转移过来,然后算从这些区间走过来的 \(w\) 转移,模板题是关路灯但是离烦没有拉。

点击查看代码
```plaintext
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for(int i = j; i <= k; ++i)
#define pre(i, j, k) for(int i = j; i >= k; --i)

using namespace std;
using LL = long long;

const int N = 4e2 + 5;
const int M = 31;
LL n, m, L, s, f[N][N][N][2], g[N], ret;

struct node {
	LL x, t;
	bool operator < (const node &a) const {
		return t < a.t;
	}
} a[N];

void init() {

}

/*
f[l][r][x][0/1] 表示 [l,r] 收集了 x 顺/逆时针走所需的最小时间
*/

void sol() {
	cin >> n >> L, m = n * 2 + 1;
	rep(i, 1, n) cin >> a[i].x;
	rep(i, 1, n) cin >> a[i].t, a[i + n + 1].x = a[i].x + L, a[i + n + 1].t = a[i].t;
//	sort(a + 1, a + 1 + n);
	a[n + 1].x = L;

	memset(f, 0x3f, sizeof f), f[n + 1][n + 1][0][0] = f[n + 1][n + 1][0][1] = 0;
//	cerr << "debug";
	rep(len, 2, n + 1) {
		rep(l, 1, m - len + 1) {
			int r = l + len - 1, x1, x2;
			x1 = a[l + 1].x - a[l].x, x2 = a[r].x - a[l].x;
			rep(p, 0, len) {
				f[l][r][p][0] = min(f[l + 1][r][p][0] + x1, f[l + 1][r][p][1] + x2);
				if(p) {
					if(f[l + 1][r][p - 1][0] + x1 <= a[l].t) f[l][r][p][0] = min(f[l][r][p][0], f[l + 1][r][p - 1][0] + x1);
					if(f[l + 1][r][p - 1][1] + x2 <= a[l].t) f[l][r][p][0] = min(f[l][r][p][0], f[l + 1][r][p - 1][1] + x2);
				}
			}
			x1 = a[r].x - a[r - 1].x, x2 = a[r].x - a[l].x;
			rep(p, 0, len) {
				f[l][r][p][1] = min(f[l][r - 1][p][1] + x1, f[l][r - 1][p][0] + x2);
				if(p) {
					if(f[l][r - 1][p - 1][1] + x1 <= a[r].t) f[l][r][p][1] = min(f[l][r][p][1], f[l][r - 1][p - 1][1] + x1);
					if(f[l][r - 1][p - 1][0] + x2 <= a[r].t) f[l][r][p][1] = min(f[l][r][p][1], f[l][r - 1][p - 1][0] + x2);
				}
			}
		}
	}
	pre(i, n, 0) {
		rep(len, 2, n + 1) {
			rep(l, 1, m - len + 1) {
				int r = l + len - 1;
				if(min(f[l][r][i][0], f[l][r][i][1]) < 1e14) cout << i, exit(0);
			}
		}
	}
}

signed main() {
#ifdef Xdik

#else
//	freopen(".in", "r", stdin);
//    freopen(".out", "w", stdout);
#endif
	FASTIO;
	int _;
	_ = 1;
	while(_--) sol();
	return 0;
}

P2466

跟上面那道题一样的。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for(int i = j; i <= k; ++i)
#define pre(i, j, k) for(int i = j; i >= k; --i)

using namespace std;
using LL = long long;

const int N = 1e3 + 5;
const int M = 31;
LL n, m, L, s, f[N][N][2], g[N], ret, suf[N], pre[N];

struct node {
	LL x, y, v;
	bool operator < (const node &a) const { return x < a.x; }
} a[N];

void init() {

}

/*
f[l][r][0/1] 表示 [l,r] 左/右行动分数最大值
*/

void sol() {
	cin >> n >> s; memset(f, 0xcf, sizeof f);
	rep(i, 1, n) cin >> a[i].x, a[i].x -= s;
	rep(i, 1, n) cin >> a[i].y;
	rep(i, 1, n) cin >> a[i].v;
	a[++n] = {0, 0, 0};
	sort(a + 1, a + 1 + n);
	rep(i, 1, n) if(a[i].x == 0) f[i][i][0] = f[i][i][1] = 0;
	rep(i, 1, n) pre[i] = pre[i - 1] + a[i].v;
	pre(i, n, 1) suf[i] = suf[i + 1] + a[i].v;
	rep(len, 2, n) {
		rep(l, 1, n - len + 1) {
			LL r = l + len - 1, x1, x2, pp;
			x1 = a[l + 1].x - a[l].x, x2 = a[r].x - a[l].x, pp = pre[l] + suf[r + 1];
			f[l][r][0] = max(f[l + 1][r][0] - x1 * pp, f[l + 1][r][1] - x2 * pp) + a[l].y;
			x1 = a[r].x - a[r - 1].x, x2 = a[r].x - a[l].x, pp = pre[l - 1] + suf[r];
			f[l][r][1] = max(f[l][r - 1][1] - x1 * pp, f[l][r - 1][0] - x2 * pp) + a[r].y;
		}
	}
	cout << fixed << setprecision(3) << 1.0 * max(f[1][n][0], f[1][n][1]) / 1000;
}

signed main() {
#ifdef Xdik

#else
//	freopen(".in", "r", stdin);
//    freopen(".out", "w", stdout);
#endif
	FASTIO;
	int _;
	_ = 1;
	while(_--) sol();
	return 0;
}

ABC219H

场切了,弱化版可以烧到负数的做法是简单关路灯问题,这个题要避免烧到负数就再加一维限制 \(k\) 表示区间外面还有 \(k\) 支还没熄灭的的蜡烛,转移也很简单。

2025/3/24

一类区间压缩类的区间 dp,典题是 P4302,mjl 初一的时候拉过跟这个一模一样的,但是那个题要输出方案,当时我不会,超的我同桌的,他那个写的贼长,不知道怎么想出来的做法,现在重新看了一遍发现很 easy 啊。

P2470

可以嵌套循环,然后又有个什么 M,那就定义 \(f_{l,r,0/1}\) 表示区间 \([l,r]\) 有没有 M 的情况下长度压缩的最小值。

要跟 P4302 一样枚举一个区间左右两半是否相同,如果可以相同则有一个转移

\[f_{l,r,0}=min(f_{l,r,0},f_{l,mid,0}+1) \]

\(mid\) 是区间中点,意义是将这两段一样的用一个 B 压缩。

2025/3/25

神秘人讲背包。

P4241

CF183D

P9669

2025/3/27

P4241

link

2025/3/31

P4657

很神奇的题,题目本质是求一条路径上选择至多 \(m\) 个点使得这些点周围的点权和最大。

朴素做法是定义 \(f_{x,i}\) 表示当前以 \(x\) 为根选了 \(i\) 个点答案最大值,转移很简单。

复杂度是 \(O(N^2 M)\) 的。

优化是直接定义 \(f_{x,i,0/1}\) 表示当前以 \(x\) 为根选了 \(i\) 个点向上/下走过来答案最大值,转移式子也很显然,注意要正反各跑一遍,还要算上向上/向下已经选了 \(m\) 个点的答案。

点击查看代码
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(nullptr), cout.tie(nullptr)
#define rep(i, j, k) for (int i = j; i <= k; ++i)
#define pre(i, j, k) for (int i = j; i >= k; --i)
#define PII pair<int, int>
#define fi first
#define se second
#define pb push_back

using LL = long long;
using namespace std;

const int N = 1e5 + 5;
const int M = 105;
const int mod = 1e9 + 7;

#define cmax(a, b) (a = a > b ? a : b)
#define cmin(a, b) (a = a < b ? a : b)

LL n, m, a[N], w[N], u, v, ret, f[N][M][2], g[N][M][2];
vector<int> e[N];
//void dfs1(int x, int fa) {
//    LL s = 0;
//    rep(i, 1, m) f[x][i] = f[x][i] = 0;
//    for(auto y : e[x]) if(y != fa) s += a[y];
//    for(auto y : e[x]) {
//        if(y == fa) continue;
//        dfs1(y, x);
//        rep (i, 1, m) {
//            f[x][i] = max({f[x][i], f[y][i], f[y][i - 1] + s});
//            f[x][i] = max(f[x][i], f[y][i]);
//        }
//    }
//}

/*
f[x][i][0/1] 表示以 x 为根用了 i 个磁铁某个点向上/下走到 x 的最大值
*/

void dfs2(int x, int fa) {
    LL s = 0;
    int s1 = 0, s2 = 0;
    rep(i, 1, m) f[x][i][0] = w[x], f[x][i][1] = w[x] - a[fa];
    for(auto y : e[x]) {
        if(y == fa) continue;
        dfs2(y, x);
        rep(i, 1, m - 1) cmax(ret, f[x][i][0] + f[y][m - i][1]);
        rep(i, 1, m) {
            cmax(f[x][i][0], max(f[y][i][0], f[y][i - 1][0] + w[x] - a[y]));
            cmax(f[x][i][1], max(f[y][i][1], f[y][i - 1][1] + w[x] - a[fa]));
        }
    }
    rep(i, 1, m) f[x][i][0] = w[x], f[x][i][1] = w[x] - a[fa];
    reverse(e[x].begin(), e[x].end());
    for(auto y : e[x]) {
        if(y == fa) continue;
        rep(i, 1, m - 1) cmax(ret, f[x][i][0] + f[y][m - i][1]);
        rep(i, 1, m) {
            cmax(f[x][i][0], max(f[y][i][0], f[y][i - 1][0] + w[x] - a[y]));
            cmax(f[x][i][1], max(f[y][i][1], f[y][i - 1][1] + w[x] - a[fa]));
        }
    }
    cmax(ret, max(f[x][m][0], f[x][m][1]));
}


signed main() {
    FASTIO;
    cin >> n >> m;
    rep(i, 1, n) cin >> a[i];
    rep(i, 1, n - 1) cin >> u >> v, e[u].pb(v), e[v].pb(u), w[v] += a[u], w[u] += a[v];
    dfs2(1, 0);
    cout << ret;
    return 0;
}
posted @ 2025-03-18 21:44  Iron_Spade  阅读(18)  评论(0)    收藏  举报