Codeforces Round #854 by cybercats (Div. 1 + Div. 2)
Codeforces Round #854 by cybercats (Div. 1 + Div. 2)
A. Recent Actions
按顺序(从上到下给了n个数),现在有m个操作,每次操作会对x进行如下操作:
- 1 如果x在当前的n个数内,则将x提到最上方
- 2 如果x不在当前n个数内,将x放在最上方,将最下方的数弹出
题目保证了x不会是开始的n个数之一。
模拟一下即可,边输入x时边做操作,记录一个mp,为x之前是否出现过,如果出现过,那么该轮不会弹出n,否则最下面的n将会被弹出,记录当前的i即为答案。
void solve(int caseT = 0) {
int n, m;
cin >> n >> m;
map<int, int> mp;
vector<int> ans;
for (int i = 1; i <= m; i++) {
int x;
cin >> x;
if (mp[x]) continue;
mp[x]++;
ans.pb(i);
}
int t = n - SZ(ans);
for (int i = 1; i <= t; i++) cout << "-1 ";
vector<int> tt;
for (int i = 0; i < SZ(ans) && i < n; i++) {
tt.pb(ans[i]);
}
for (int i = SZ(tt)-1; i >= 0; i--) {
cout << tt[i] << " ";
}
cout << "\n";
}
B. Equalize by Divide
给定一个序列,每次可以选择其中任意两个数做:
首先:如果a已经全相等则不需要操作,其次可以分析出答案不可能小于2(除非a全1),因为对于一个序列我们要得到一个全1的序列需要怎么做呢,一种简单的做法是,选择一个最大的数,然后对其它所有数做上述过程,那么其它数会变成1,最大的数永远无法变成1。同时由此得出解决思路:
- 每次选择最小最大的数,进行这个操作(对大数操作,因为答案只会小不会大),直到最小值等于最大值,或者出现了1,操作结束。
最小值最大值可以用优先队列来维护。
void solve(int caseT = 0) {
int n;
cin >> n;
vector<P(int)> a(n + 1, {0, 0});
vector<P(int)> ans;
map<int, int> mp;
int id = -1;
for (int i = 1; i <= n; i++) {
cin >> a[i].fi;
a[i].se = i;
mp[a[i].fi]++;
}
sort(all(a));
if (SZ(mp) == 1) {
cout << "0\n";
return ;
}
priority_queue<P(int), vector<P(int)>, greater<P(int)>> mn;
priority_queue<P(int), vector<P(int)>, less<P(int)>> mx;
for (int i = 1; i <= n; i++) {
mn.push(a[i]);
mx.push(a[i]);
}
while (1) {
auto mnn = mn.top();
auto mxx = mx.top(); mx.pop();
if (mnn.fi == 1) {
cout << "-1\n";
return ;
}
if (mnn.fi == mxx.fi) break;
int x = mxx.fi / mnn.fi + (mxx.fi % mnn.fi != 0);
mn.push({x, mxx.se});
mx.push({x, mxx.se});
ans.pb({mxx.se, mnn.se});
}
cout << SZ(ans) << '\n';
for (auto [x, y] : ans) {
cout << x << " " << y << '\n';
}
}
D1. Hot Start Up (easy version)
题目
有两个CPU处理n个问题,问题序列为A必须按顺序执行,且A[i]必须等A[i-1]执行完才能执行,每个任务有自己的种类type,一共K个种类,种类为i的任务运行所花费的时间为cold[i],如果运行这个任务的CPU运行的上一个任务也是i类,那么这个任务花费的时间为hot[i], 保证hot[i]一定小于cold[i];
简单来说就是: 如果cpu 1 连续运行了 x, y,y的时间计算如下:
- 如果 type[x]==type[y], 那么y花费 cold[type[y]]
- 如果 type[x]!=type[y], 那么y花费 hot[type[y]]
想到了dp,当时想的是:处理完i后,使用的是cpu1/2, 结尾是j的最小花费,没想到正确的转移方程,但是结果和样例大部分相同,直接debug到结束,寄。
积累下dp模型:
问题: 将一个序列分为两个序列,使求得的某个贡献最大。
一般性通解:定义 dp[i][j]为放置完第i个数后,另外一个结尾是j的最大值(要求的值)。
则,这题的状态定义为:
- 放置完第i个数后,另外一个CPU处理的最后一个任务是j的最小花费时间。
状态转移:
- 放在i-1后面
- 不放在i-1的后面
时间复杂度
const int N = 5010;
void solve(int caseT = 0) {
int n, k;
cin >> n >> k;
vector<ll> cold(k+1, 0), hot(k+1, 0), a(n+1, 0);
vector<vector<ll>> dp(n + 1, vector<ll>(k + 1, inf));
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= k; i++) cin >> cold[i];
for (int i = 1; i <= k; i++) cin >> hot[i];
dp[1][0] = cold[a[1]];
for (int i = 1; i <= n; i++) {
// 放在 i - 1 的后面
for (int j = 0; j <= k; j++) {
ll cost;
if (a[i] != a[i - 1]) cost = cold[a[i]];
else cost = hot[a[i]];
dp[i][j] = min(dp[i][j], dp[i - 1][j] + cost);
}
// 不放在 i - 1 后面
for (int j = 0; j <= k; j++) {
ll cost;
if (j == a[i]) cost = hot[a[i]];
else cost = cold[a[i]];
dp[i][a[i - 1]] = min(dp[i][a[i - 1]], dp[i - 1][j] + cost);
}
}
ll ans = inf;
for (int i = 0; i <= k; i++) ans = min(ans, dp[n][i]);
cout << ans << '\n';
}
D2. Hot Start Up (hard version)
观察上面的转移方程,第一维i,可以通过滚动数组滚掉,空间复杂度可以优化到
时间复杂度如何优化?观察转移方程,发现:
(1) 放在i-1的后面,相当于给dp[j]全加上了cost
(2) 不放在i-1的后面,相当于查询了min(dp[j]+cost) 并修改 dp[a[i-1]]的值
则(1)区间加操作,(2)单点查询+单点修改操作。
用线段树维护dp[0~k]的区间最小值
总的时间复杂度为
优化完第一维,记得要先将信息查询出来,然后再进行修改操作。
const ll inf = 2e18;
const int N = 300010, INF = 2e9;
int n, k;
int cold[N], hot[N], a[N];
struct info {
ll val, minx;
};
info operator + (const info L, const info R) {
ll minx = min(L.minx, R.minx);
ll val = L.val + R.val;
return info{val, minx};
}
struct seg {
info val;
ll tag;
ll sz;
} tr[N * 8];
void pushup(int p) {
tr[p].val = tr[lp].val + tr[rp].val;
}
void settag(int p, ll tag) {
tr[p].val.minx += tag;
tr[p].val.val += tag * tr[p].sz;
tr[p].tag += tag;
}
void pushdown(int p) {
if (tr[p].tag) {
settag(lp, tr[p].tag);
settag(rp, tr[p].tag);
tr[p].tag = 0;
}
}
void build(int p, int l, int r) {
tr[p].sz = r - l + 1;
if (l == r) {
tr[p].val = {inf, inf};
tr[p].tag = 0;
} else {
int mid = (l + r) / 2;
build(lp, l, mid), build(rp, mid + 1, r);
pushup(p);
}
}
void modify(int p, int l, int r, int L, int R, ll d) {
if (L <= l && r <= R) {
settag(p, d);
} else {
pushdown(p);
int mid = (l + r) / 2;
if (L <= mid) modify(lp, l, mid, L, R, d);
if (R > mid) modify(rp, mid + 1, r, L, R, d);
pushup(p);
}
}
void modify(int p, int l, int r, int x, ll d) {
if (l == r) {
ll t = min(tr[p].val.val, 1ll * d);
tr[p].val = {t, t};
} else {
pushdown(p);
int mid = (l + r) / 2;
if (x <= mid) modify(lp, l, mid, x, d);
else modify(rp, mid + 1, r, x, d);
pushup(p);
}
}
ll query(int p, int l, int r, int x) {
if (l == r) return tr[p].val.val;
else {
pushdown(p);
int mid = (l + r) / 2;
if (x <= mid) return query(lp, l, mid, x);
else return query(rp, mid + 1, r, x);
}
}
void q(int p, int l, int r) {
pushdown(p);
if (l == r) {
} else {
int mid = (l + r) / 2;
q(lp, l, mid);
q(rp, mid+1,r);
pushup(p);
}
}
void solve(int caseT = 0) {
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= k; i++) cin >> cold[i];
for (int i = 1; i <= k; i++) cin >> hot[i];
build(1, 0, k);
modify(1, 0, k, 0, cold[a[1]]);
for (int i = 2; i <= n; i++) {
ll y = tr[1].val.minx + cold[a[i]];
ll x = query(1, 0, k, a[i]) + hot[a[i]];
ll now = min(x, y);
// 放在 i - 1 的后面
// do: dp[i][k] = min(dp[i][k], dp[i - 1][k] + cost);
int cost;
if (a[i] == a[i - 1]) cost = hot[a[i]];
else cost = cold[a[i]];
modify(1, 0, k, 0, k, cost);
// 不放在 i - 1 后面
// do: dp[i][a[i-1]] = min(dp[i][a[i-1]], dp[i-1][j] + cost)
modify(1, 0, k, a[i - 1], now);
}
q(1, 0 ,k);
cout << tr[1].val.minx << '\n';
}
本文作者:rufu
本文链接:https://www.cnblogs.com/rufu/p/17170447.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步