Educational Codeforces Round 150 (Rated for Div. 2) 感想



感想

其实我不会写D。我一看出题人BledDest,我就知道我完了。

A、Game with Board

1.题目内容

题目地址

2.个人思路

在n大于4之后,alice只要给bob留两个1就必胜,否则就是bob必胜(可以手推一下)。

3.代码


int main() {
	cin >> t;
	while (t--) {
		cin >> n;
		if (n <= 4) {
			cout << "Bob\n";
		}
		else {
			cout << "Alice\n";
		}
	}
	return 0;
}

B、Keep it Beautiful

1.题目内容

题目地址

2.个人思路

暴力处理,第一个数字肯定要加入,然后如果维持单调递增就一直加入,当出现小于当前数字的值之后进行判断,如果它小于等于第一个数字就加入,否则就不加入。
加入第一个非递增数字后,后面的数字也应该单调递增且小于等于第一个数字并大于等于第一个非递增数字。

3.代码

这个写的比较难看。


ll t, n, m, p = 998244353, k;
ll arr[200005], cz[200005];
 
int main() {
	cin >> t;
	while (t--) {
		cin >> n;
		for (int i = 1; i <= n; i++) {
			scanf_s("%lld", &arr[i]);
		}
		vector<ll> ans;
		ll mn = arr[1], len = 0, stg = 0;
		string s;
		s += '1';
		ans.push_back(arr[1]);
		for (int i = 2; i <= n; i++) {
			if (arr[i] >= ans[len]) {
				if (!stg) {
					ans.push_back(arr[i]);
					s += '1';
					len++;
				}
				else {
					if (arr[i] >= mn && arr[i] <= ans[0]) {
						ans.push_back(arr[i]);
						s += '1';
						len++;
					}
					else s += '0';
				}
			}
			else if(arr[i] <= ans[0]) {
				if (!stg) {
					stg = 1;
					ans.push_back(arr[i]);
					s += '1';
					len++;
				}
				else s += '0';
			}
			else s += '0';
			mn = min(arr[i], mn);
		}
		cout << s << "\n";
	}
	return 0;
}

C、Ranom Numbers

1.题目内容

题目地址

2.个人思路

tn保存倒序处理从n到i的表示的数字, mn表示倒叙从n到i的最大值。
如果最大值与当前值不一样就减掉,否则就加上。
然后正序处理nn。
nn[i][j]表示到第i位的时候,后面的最大值为j的结果。
转移需要找到第一个大于等于j的数字位置pos,然后将(pos, i - 1)的区间和全部减掉,再加上pos处取对应字母的结果。
最后枚举每一位从a取到e的结果,
答案就是nn[i][max(j, mn[i + 1])] + tn[i + 1]的最大值。

3.代码

这个写的更难看了。

struct com {
	ll d;
	ll h;
	ll p;
	bool operator < (const com& b) const {
		if (b.d == d && b.h == h) return p < b.p;
		if (b.d == d) return h < b.h;
		return d < b.d;
	}
}lis[300005];
 
ll t, n, m, p = 998244353;
ll arr[200005], tn[200005], mn[200005];
ll nn[200005][5], sum[200005];
ll pp[5] = { 1, 10, 100, 1000, 10000 };
 
int main() {
	cin >> t;
	while (t--) {
		string s;
		cin >> s;
		n = s.size();
		ll ans = -2147483647ll * 100000ll;
		ll st = 0, tp[5] = {-1, -1, -1, -1, -1};
		for (int i = 0; i <= n; i++) {
			for (int j = 0; j < 5; j++) {
				nn[i][j] = 0;
			}
			mn[i] = 0;
			tn[i] = 0;
			sum[i] = 0;
		}
		for (int i = n - 1; i >= 0; i--) {
			mn[i] = max(mn[i + 1], (ll)s[i] - 'A');
			if (mn[i] != (s[i] - 'A')) {
				tn[i] = tn[i + 1] - pp[s[i] - 'A'];
			}
			else tn[i] = tn[i + 1] + pp[s[i] - 'A'];
		}
 
		for (int i = 0; i < n; i++) {
			ll tmp = 0, t1 = 10000, t2 = 0;
			for (int j = 0; j < 5; j++) {
				ll pos = -1, tar = -1;
				for (int k = 0; k < 5; k++) {
					if (k >= j) {
						if (pos < tp[k]) {
							pos = tp[k];
							tar = k;
						}
					}
				}
				if (pos != -1) {
					nn[i][j] = pp[j] - sum[i - 1] + sum[pos] + nn[pos][tar];
				}
				else nn[i][j] = pp[j] - sum[i - 1];
			}
			tp[s[i] - 'A'] = i;
			if (i > 0) sum[i] = sum[i - 1];
			sum[i] += pp[(s[i] - 'A')];
		}
 
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < 5; j++) {
				ll t1 = max(mn[i + 1], (ll)j);
				ans = max(ans, tn[i + 1] + nn[i][t1]);
			}
		}
		cout << ans << "\n";
	}
	return 0;
}

D、Pairs of Segments

1.题目内容

题目地址

2.个人思路

我不会,打acm的大佬同学教我的。
这个题的好像思路很多,怎么处理都可以,我这里只提供个人理解的方法。
按照右端点排序,dp[i]为到第i位为止的最大数量。
状态转移肯定就是下面这两种情况。

dp[i] = max(dp[j] + 2, dp[i]);

dp[i] = max(dp[j], dp[i]);

考虑什么时候+2,什么时候不+2。
很显然,如果有区间能和i构成+2,且这个区间的左端点和i的左端点都不和j相交,就可以转移并+2。
我画了几个图,可以参考下,假设i = 3,j = 1。
肯定不行
肯定不行
行

只需要处理出i - 1到j + 1之间跟i相交的最大左端点距离,然后判断这个最大左端点是否大于j的右端点即可。最优状态一定会移动到n。
时间复杂度为 O ( n 2 ) O(n^2) O(n2)

3.代码

struct com {
	ll d;
	ll h;
	ll p;
	bool operator < (const com& b) const {
		if (b.d == d && b.h == h) return p < b.p;
		if (b.d == d) return h < b.h;
		return d < b.d;
	}
}lis[200005], ord[200005];
 
ll t, n, m, p = 998244353;
vector<ll> gra[2005];
unordered_set<ll> st;
ll dp[2005], mn[2005][2005];
 
int main() {
	cin >> t;
	while (t--) {
		cin >> n;
		for (int i = 1; i <= n; i++) {
			cin >> lis[i].d >> lis[i].h;
			lis[i].p = i;
			ord[i] = { lis[i].h , lis[i].d , lis[i].p };
			dp[i] = 0;
			mn[i][i + 1] = -1;
		}
		mn[0][1] = -1;
		for (int i = n; i >= 0; i--) {
			for (int j = i; j >= 0; j--) {
				mn[i][j] = -1;
			}
		}
		sort(ord + 1, ord + 1 + n);
		ord[0] = { -1, -1, 0 };
		for (int i = n; i > 0; i--) {
			for (int j = i; j > 0; j--) {
				if (ord[j].d >= ord[i + 1].h) {
					mn[i][j] = max(mn[i][j + 1], ord[j].h);
				}
				else mn[i][j] = mn[i][j + 1];
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j < i; j++) {
				if (ord[j].d < mn[i - 1][j + 1] && ord[j].d < ord[i].h) {
					dp[i] = max(dp[j] + 2, dp[i]);
				}
				else {
					dp[i] = max(dp[j], dp[i]);
				}
			}
		}
		ll ans = 2147483647;
		ans = n - dp[n];
		cout << ans << "\n";
	}
	return 0;
}

E. Fill the Matrix

1.题目内容

题目地址

2.个人思路

硬找所有可以插数字的区间,vector用来记录到第i行时可以开始插入数字的列下标。le,re分别记录区间的左端点和右端点。
每个列下标只会修改最多两个区间,然后分情况讨论。
两边都没区间,那么新增一个长度为1的区间。
左边是右边不是,和左边的合并。
右边是左边不是,和右边的合并。
两边都是,两边合并。
每次都需要记录当前行所有区间的长度,并将结果加到总记录cnt上。
最后按长度从大到小往里放数字就可以。
其实就是看[1, m]个区间最少被成多少份的问题。
理论最差时间复杂度为 O ( n n ) O(n\sqrt{n}) O(nn )
因为同时出现的不同区间长度最多为 n \sqrt{n} n 个。

3.代码

废话文学代码,其实可以合并优化优化.


ll t, n, m, p = 998244353;
ll arr[200005], tmp[200005], cnt[200005];
unordered_map<ll, ll> le, re;
vector<ll> all[200005];
 
int main() {
	cin >> t;
	while (t--) {
		scanf_s("%lld", &n);
		for (int i = 1; i <= n + 1; i++) {
			all[i].clear();
			tmp[i] = 0;
			cnt[i] = 0;
		}
		for (int i = 1; i <= n; i++) {
			scanf_s("%lld", &arr[i]);
			all[arr[i] + 1].push_back(i);
		}
		cin >> m;
		le.clear();
		re.clear();
		unordered_map<ll, ll> len;
		for (int i = 1; i <= n; i++) {
			for (int j = 0; j < all[i].size(); j++) {
				ll x = all[i][j];
				ll ttt = i, i = x;
				if (tmp[i - 1] == 0 && tmp[i + 1] == 0) {
					le[i] = i;
					re[i] = i;
					len[1]++;
				}
				else if (tmp[i - 1] == 1 && tmp[i + 1] == 0) {
					ll t1 = re[(ll)i - 1], l = (ll)i - t1;
					re.erase((ll)i - 1);
					re[i] = t1;
					le[t1] = i;
					len[l]--;
					if (len[l] == 0) len.erase(l);
					len[l + 1]++;
				}
				else if (tmp[i - 1] == 0 && tmp[i + 1] == 1) {
					ll t1 = le[(ll)i + 1], l = t1 - i;
					le.erase((ll)i + 1);
					le[i] = t1;
					re[t1] = i;
					len[l]--;
					if (len[l] == 0) len.erase(l);
					len[l + 1]++;
				}
				else {
					ll t2 = le[(ll)i + 1], t1 = re[(ll)i - 1];
					ll l1 = (ll)i - t1, l2 = t2 - i;
					re.erase((ll)i - 1);
					le.erase((ll)i + 1);
					le[t1] = t2;
					re[t2] = t1;
					len[l1]--;
					if (len[l1] == 0) len.erase(l1);
					len[l2]--;
					if (len[l2] == 0) len.erase(l2);
					len[t2 - t1 + 1]++;
				}
				i = ttt;
				tmp[x] = 1;
			}
			for (auto x : len) {
				cnt[x.first] += x.second;
			}
		}
		ll ans = m, tt = m;
		for (int i = n; i > 0; i--) {
			if (!tt) break;
			ans -= min(cnt[i], tt / i + (tt % i != 0));
			tt -= min(tt, cnt[i] * i);
		}
		cout << ans << "\n";
	}
	return 0;
}

总结

同学真的好强。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值