为了能到远方,脚下的每一步都不能|

园龄:粉丝:关注:

2023-03-02 00:23阅读: 201评论: 0推荐: 1

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[i]=a[i]a[j],ij;问最后序列是否可以全相等,给出构造序列。

首先:如果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后面

dp[i][j]=min(dp[i][j],dp[i1][j]+cost);0<=j<=k;

a[i]==a[i1],cost=hot[a[i]];cost=cold[a[i]];

  • 不放在i-1的后面

dp[i][a[i1]]=min(dp[i][a[i1]],dp[i1][j]+cost);0<=j<=k;

j==a[i]cost=hot[a[i]];cost=cold[a[i]];

ansmin(dp[n][i]);

时间复杂度O(nk),n,k5000

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)

n,k300000

观察上面的转移方程,第一维i,可以通过滚动数组滚掉,空间复杂度可以优化到O(N)

时间复杂度如何优化?观察转移方程,发现:

(1) 放在i-1的后面,相当于给dp[j]全加上了cost

(2) 不放在i-1的后面,相当于查询了min(dp[j]+cost) 并修改 dp[a[i-1]]的值

则(1)区间加操作,(2)单点查询+单点修改操作。

用线段树维护dp[0~k]的区间最小值

总的时间复杂度为 O(NlogK)

优化完第一维,记得要先将信息查询出来,然后再进行修改操作。

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 中国大陆许可协议进行许可。

posted @   迷糊的Rufu  阅读(201)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起