Codeforces Round #797 (Div. 3) D, E, F, G题题解

Codeforces题解汇总

Round #797 div3

D. Black and White Stripe

固定长度最大子段和, 简单只贴代码了

Solution

#include<bits/stdc++.h>
typedef long long ll;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef unsigned long long ull;
typedef double db;
#define arr(x) (x).begin(),(x).end()
#define x first
#define y second
#define pb push_back
#define mkp make_pair
#define endl "\n"
using namespace std;

int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--){
		int n, k;
		cin >> n >> k;
		vector<int> pre(n + 1, 0);
		string s;
		cin >> s;
		s = " " + s;
		for(int i = 1; i <= n; i++){
			pre[i] = pre[i - 1] + (s[i] == 'B');
		}
		int ans = n;
		for(int i = k; i <= n; i++){
			ans = min(ans, k - (pre[i] - pre[i - k]));
		}
		cout << ans << endl;
	}
    return 0;
}

E. Price Maximization

双指针, 贪心, 排序, 或者 STL

题意

给了 \(n\) 个货品, \(n\) 是偶数, 每个货品有价值 \(a_i\) , 将货品两两配对, 两两配对和总价值为 \(w_i\), 给定 \(k\), 求 \(\Sigma \lfloor \frac{w_i}{k} \rfloor\) 最大值

数据范围
\(2\leq n \leq 2*10^5\)
\(1\leq k \leq 1000\)
\(0\leq a_i \leq 10^9\)

思路

  • 观察数据范围 , \(k\leq 1000\).
  • 由于答案为重量之和向下取整, 对 \(a_i\) 来说, 无论怎样组合, \(a_i / k\) 的贡献是确定的.
  • 这样我们只用关心 \(a_i \% k\) 的贡献, 贪心 地来想, 对于 \(x=a_i\% k\) , 需要找到满足最小余数 \(\geq k-x\)
  • 可以用两种方式实现
    • 一种是显然的双指针 (呵呵我写挂了, 很离谱, 有时间来补这个解法)
    • 还有一种是 jiangly超人 的 STL 做法(复杂度稍高但聊胜于无)

Solution

#include<bits/stdc++.h>
typedef long long ll;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef unsigned long long ull;
typedef double db;
#define arr(x) (x).begin(),(x).end()
#define x first
#define y second
#define pb push_back
#define mkp make_pair
#define endl "\n"
using namespace std;

int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--){
		int n, k;
		cin >> n >> k;
		ll ans = 0;
		multiset<int> s;
		for(int i = 0; i < n; i++){
			int x;
			cin >> x;
			ans += x / k;
			s.insert(x % k);
		}
		while(!s.empty()){
			auto x = *s.begin();
			s.erase(s.begin());
			auto it = s.lower_bound(k - x);
			if(it != s.end()){
				s.erase(it);
				ans++;
			}
		}
		cout << ans << endl;
	}
    return 0;
}

F. Shifting String

置换, 环, 字符串最小循环节, LCM

题意

给定一个长度为 \(n\) 的字符串 \(s\), 和一个排列 \(P\) , 每次操作让 \(s_i = s_{P_i}\) , 询问至少经过多少次操作能让字符串与初始状态相同

数据范围
\(1\leq n \leq 200\)
\(1\leq p_i \leq n\)

思路

  • 草稿纸上画几遍, 发现可能与置换中所形成的各个环的长度有关系, 这个关系是各长度的 \(LCM\)
  • 答案其实是与每个环经过多少次变换能变成原来的字符串, 由于是字符串关系, 这个多少次变换, 其实就是每个环上对应字符串的最小循环节.
  • 所以答案就是各个环最小循环节长度的 \(LCM\)
  • 如何求最小循环节? 此题可以暴力枚举循环节长度判断是否合法, 数据规模较大可以使用 KMP 求字符串最小循环节, 得到 \(ne\) 数组判断 if(len % (len - ne[len]) == 0)

Solution

#include<bits/stdc++.h>
typedef long long ll;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef unsigned long long ull;
typedef double db;
#define arr(x) (x).begin(),(x).end()
#define x first
#define y second
#define pb push_back
#define mkp make_pair
#define endl "\n"
using namespace std;
const int N = 210;
ll gcd(ll a, ll b){
	return b ? gcd(b, a % b) : a;
}

ll LCM(ll a, ll b){
	return a / gcd(a, b) * b;
}

int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--){
		int n;
		string s;
		cin >> n >> s;
		s = " " + s;
		vector<bool> st(n + 1, false);
		vector<int> p(n + 1,0);
		for(int i = 1; i <= n; i++)
			cin >> p[i];
		ll ans = 1;
		for(int i = 1; i <= n; i++){
			if(st[i]) continue;
			string tmp = " ";
			int sz = 0;
			for(int j = i; !st[j]; j = p[j]){
				st[j] = true;
				tmp += s[j];
				sz++;
			}
			vector<int> ne(sz + 1, 0);
			ne[1] = 0;
			for(int i = 2, j = 0; i <= sz; i++){
				while(j && tmp[j + 1] != tmp[i]) j = ne[j];
				if(tmp[i] == tmp[j + 1]) j++;
				ne[i] = j;
			}
			ll len = sz - ne[sz];
			if(sz % len == 0)
				ans = LCM(ans, len);
			else
				ans = LCM(ans, sz);
		}
		cout << ans << endl;
	}
    return 0;
}

G. Count the Trains

STL, 二分, 思维

题意

给了 \(n\) 个点, 每个点有值为 \(a_i\) , 从左往右形成序列, 对于序列上下标 \(i\) 位置, 值为 \(min\{a_j\}, (j\leq i)\)

\(m\) 次操作, 输入 \(k, d\) , 每次操作对 a[k] -= d , 询问每次操作完之后, 序列中有多少个不同的数 ?

思路

  • 容易挖掘题目性质, 对于一个数 \(a_i\) , 如果 \(\exist j < i, a_j\leq a_i\) , 那么 \(a_i\) 不会对答案产生贡献
  • 由于答案是求不同的数个数 , 我们可以联想到 map, set 这一类 STL 容器来解决, 那么问题是如何实现.
  • 参照第一点, 我们尝试用 map<int,int> 容器来实现, 第一关键字为下标, 第二关键字为对应值
  • 每次输入或操作对 map 中插入 i, a[i], 若前面迭代器元素值小于等于 \(a_i\) 则删除现在插入的迭代器, 若没有删除, 检查后面的迭代器是否满足要求(next(it)->second <= it->second)
  • 实现过程中注意代码细节, 防止越界等错误, 时间复杂度为 \(O(n+m)log(n+m))\) , 参考 yyds的jiangly

Solution

#include<bits/stdc++.h>
typedef long long ll;
typedef std::pair<int, int> PII;
typedef std::pair<ll, ll> PLL;
typedef unsigned long long ull;
typedef double db;
#define arr(x) (x).begin(),(x).end()
#define x first
#define y second
#define pb push_back
#define mkp make_pair
#define endl "\n"
using namespace std;
map<int, int> mp;

void add(int i, int x){
	mp[i] = x;
	auto it = mp.find(i);
	if(it != mp.begin() && prev(it)->second <= it->second){
		mp.erase(it);
		return ;
	}
	while(next(it) != mp.end() && next(it)->second >= it->second)
		mp.erase(next(it));
}

int main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--){
		mp.clear();
		int n, m;
		cin >> n >> m;
		vector<int> a(n + 1, 0);
		for(int i = 1; i <= n; i++){
			cin >> a[i];
			add(i, a[i]);
		}
		while(m--){
			int k, d;
			cin >> k >> d;
			a[k] -= d;
			add(k, a[k]);
			cout << mp.size() << " ";
		}
		cout << endl;
	}
    return 0;
}
posted @ 2022-06-08 09:52  Roshin  阅读(202)  评论(0编辑  收藏  举报
-->