ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

"蔚来杯"2022牛客暑期多校训练营9

2022-08-20 20:30:40  阅读:154  来源: 互联网

标签:... int 蔚来 LL 多校 long 牛客 ans dp


比赛链接:

https://ac.nowcoder.com/acm/contest/33194

A.Car Show

题意:

长度为 \(n\) 的一个序列 \(a\),问有多少个区间中 \([1, m]\) 的数都出现过。

思路:

双指针。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL n, m;
	cin >> n >> m;
	vector <LL> a(n + 1);
	for (int i = 1; i <= n; i ++ )
		cin >> a[i];
	vector <LL> cnt(m + 1);
	LL ans = 0, sum = 0;
	for (int i = 1, j = 0; i <= n; i ++ ){
		while(sum < m && j < n){
			j ++ ;
			cnt[a[j]] ++ ;
			if (cnt[a[j]] == 1){
				sum ++ ;
			}
		}
		if (sum == m) ans += n - j + 1;
		cnt[a[i]] -- ;
		if (cnt[a[i]] == 0) sum -- ;
	}
	cout << ans << "\n";
	return 0;
}

B.Two Frogs

题意:

有 \(n\) 片荷叶,刚开始两只青蛙在第 1 片荷叶上,在第 \(i\) 片荷叶上会跳到 \([i + 1, i + a[i]]\) 这个区间中的任意一片荷叶上,问两只青蛙花同样的步数跳到第 \(n\) 片荷叶上的概率。

思路:

定义 \(dp[i][j]\) 表示跳到第 \(i\) 片荷叶上花了 \(j\) 步的概率。
每次 \(dp[i][j]\) 可以转移到 \(dp[i + 1][j], dp[i + 2][j], ... dp[i + a[i][j]\) 上,是 \(O(n^3)\) 的,可以发现如果 \(i\) 和 \(j\) 反一下的话,可以通过前缀和/差分优化掉一个 \(n\)。
于是定义 \(dp[j][i]\) 表示花了 \(j\) 步跳到第 \(i\) 片荷叶上的概率,再通过前缀和/差分去实现转移。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int P = 998244353;
LL qp(LL a, LL k, LL p){
	LL ans = 1;
	while (k){
		if (k & 1) ans = ans * a % p;
		k >>= 1;
		a = a * a % p;
	}
	return ans;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL n;
	cin >> n;
	vector <LL> inv(n + 1);
	inv[1] = 1;
	for (int i = 1; i <= n; i ++ )
		inv[i] = qp(i, P - 2, P);
	vector <LL> a(n);
	for (int i = 1; i < n; i ++ )
		cin >> a[i];
	vector dp(n + 1, vector<LL>(n + 2, 0));
	dp[0][1] = 1;
	for (int j = 0; j < n; j ++ ){
		if (j){
			for (int i = 1; i <= n; i ++ )
				dp[j][i] = (dp[j][i] + dp[j][i - 1]) % P;
		}
		for (int i = 1; i < n; i ++ ){
			dp[j + 1][i + 1] = (dp[j + 1][i + 1] + inv[a[i]] * dp[j][i] % P) % P;
			dp[j + 1][i + a[i] + 1] = (dp[j + 1][i + a[i] + 1] - inv[a[i]] * dp[j][i] % P) % P;
		}
	}
	LL ans = 0;
	for (int j = 1; j < n; j ++ )
		ans = (ans + dp[j][n] * dp[j][n] % P) % P;
	cout << ans << "\n";
	return 0;
}

E.Longest Increasing Subsequence

题意:

构造一个长度小于等于 100 的序列,使其最长上升子序列的数量等于 \(m\)。

思路:

容易想到构造 2, 1, 4, 3, 6, 5 ... \(2n\), \(2n - 1\) 这样的序列,总方案为 \(2^n\)。
将 \(m\) 转为二进制,\(a^n, a^{n - 1}, ..., a_0\),那么 \(n\) 就是二进制长度。
所以构造出来的序列已经满足了 \(2^n\)。考虑 \(a_i = 1\),要在原基础上加上这么多的最长公共子序列,可以这么操作。
2, 1, 4, 3, ..., \(2i\), \(2i - 1\), (\(b_1, b_2, ... b_k\))(添加的数), \(2(n - k + 1)\), \(2(n - k + 1) - 1\), ..., \(2n\), \(2n - 1\)。
添加了 \(k\)(后面数的数量除2) 个数,这些数要大于后面所有数,且是单调上升的,这样就可以让 \(LIS\) 的数量增加 \(2^i\) 个,同时 \(LIS\) 的长度还是 \(n\)。
其实就是用 \(k\) 个较大的数组成的一个方案替换掉后面 \(2^k\) 个方案。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	LL m;
	cin >> m;
	if (m == 1){
		cout << "1\n1\n";
		return;
	}
	vector <LL> bit;
	for (int t = m; t; t >>= 1)
		bit.push_back(t & 1);
	vector <LL> ans;
	LL len = bit.size(), cnt = 0;
	for (int i = len - 2; i >= 0; i -- ){
		cnt ++ ;
		ans.push_back(i * 2 + 1);
		ans.push_back(i * 2 + 2);
		if (bit[i]){
			while(cnt){
				ans.push_back(0);
				cnt -- ;
			}
		}
	}
	reverse(ans.begin(), ans.end());
	LL n = ans.size();
	cout << n << "\n";
	LL num = 2 * len - 1;
	for (int i = 0; i < n; i ++ ){
		if (ans[i]) cout << ans[i];
		else{
			cout << num;
			num ++ ;
		}
		cout << " \n"[i == n - 1];
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

I.The Great Wall II

题意:

有一个长为 \(n\) 的序列 \(a\),将它分成 \(k(k = 1, 2, ..., n)\) 段后,每段最大值之和最小是多少。

思路:

定义 \(dp[k][i]\) 表示用 \(k\) 段划分前 \(i\) 个数的最大值之和(后面称之为代价)的最小值。
容易得到转移方程 \(dp[k][i] = min(dp[k][j], max(a[j + 1], a[j + 2], ..., a[i])\),暴力的转移是 \(O(n^3)\),尝试优化。
如果选择了 \(a[j]\),那么所有最大值为 \(a[j]\) 的区间的 \(dp\) 值都可以转移到这里来,所以可以做一个单调递减的单调栈,维护序列中的元素划分的代价。
当 \(a[j]\) 入栈,被弹出的元素都是比它小的,即这些元素到 \(j\) 的整个区间中的最大值都是 \(a[j]\),用 \(val\) 数组记录这些元素的代价的最小值,用来进行转移。
所有弹出元素的最小代价还不是当前的答案,还漏了上一个区间的最大值的最小代价,因为是单调递减的栈,当前元素前面可能为空,也可能还有一个比它大的元素,这个元素的代价还没有计算,所以当前这个所有弹出元素求出的最小代价还要和上一个最大值区间的最小代价取一个小的才是最终答案。
例如:
4 2 1 3
3 这个数进入单调栈之后,将 2 1 弹出,记下了它们的最小值 \(x\),然后 \(x + 3\) 还要和 4 对应的最小代价取一个小的,才是 3 这个位置的最小代价。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 8e3 + 10;
LL n, a[N], f[N], g[N], stk[N], val[N], top;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i ++ ){
		cin >> a[i];
		f[i] = 1e9;
	}
	g[0] = 1e9;
	for (int k = 1; k <= n; k ++ ){
		top = 0;
		for (int i = 1; i <= n; i ++ ){
			LL x = f[i - 1];
			while(top && a[stk[top]] <= a[i]){
				x = min(x, val[top]);
				top -- ;
			}
			stk[ ++ top] = i;
			val[top] = x;
			g[i] = min(g[stk[top - 1]], x + a[i]);
		}
		for (int i = 0; i <= n; i ++ )
			f[i] = g[i];
		cout << f[n] << "\n";
	}
	return 0;
}

标签:...,int,蔚来,LL,多校,long,牛客,ans,dp
来源: https://www.cnblogs.com/Hamine/p/16601379.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有