Codeforces Round #797 (Div. 3) D, E, F, G题题解
作者:互联网
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)
- 此处给出 KMP 的代码 (依然有些参考 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;
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;
}
标签:797,typedef,int,题解,cin,long,leq,Div,define 来源: https://www.cnblogs.com/Roshin/p/cfround797div3.html