AtCoder Beginner Contest 247题解
A - Move Right
题目描述:给你一个长度为\(4\)的01
串,让你将它右移一位并将高位补0
后输出。
思路:根据题意模拟即可
时间复杂度:\(O(1)\)
参考代码:
void solve() {
string s;
cin >> s;
s = '0' + s.substr(0, 3);
cout << s << '\n';
return;
}
B - Unique Nicknames
题目描述:有\(n\)个人,每个人有两个候选的名字,问你是否存在一种分配方案,使得每个人的名字都不在其他人的候选名字中出现。
思路:考虑到数据量比较小,考虑读入之后暴力检验即可。
时间复杂度:\(O(n^2|S|)\),\(|S|\)为字符串的长度
参考代码:
void solve() {
int n;
cin >> n;
vector<vector<string>>strs(n, vector<string>(2));
for (int i = 0; i < n; ++i) cin >> strs[i][0] >> strs[i][1];
for (int i = 0; i < n; ++i) {
int cnt = 0;
for (int k = 0; k <= 1; ++k) {
for (int j = 0; j < n; ++j) {
if (i == j) continue;
if (strs[j][0] != strs[i][k] && strs[j][1] != strs[i][k]) continue;
++cnt;
break;
}
}
if (cnt == 2) {
cout << "No" << '\n';
return;
}
}
cout << "Yes" << '\n';
return;
}
C - 1 2 1 3 1 2 1
题目描述:定义序列\(S_i\),其中\(S_1 = (1)\),有递推式\(S_i = S_{i - 1}\;i\;S_{i - 1}\),现在给定\(n\),输出\(S_n\)。
思路:根据题意模拟即可。
时间复杂度:\(O(2^n)\)
参考代码:
void solve() {
int n;
cin >> n;
vector<int> res = { 1 };
for (int i = 2; i <= n; ++i) {
int m = res.size();
vector<int>ans(2 * m + 1, 0);
for (int k = 0; k < m; ++k) ans[k] = ans[m + k + 1] = res[k];
ans[m] = i;
swap(res, ans);
}
for (auto&& re : res) cout << re << ' ';
cout << '\n';
return;
}
D - Cylinder
题目描述:有一个队列,有\(Q\)次操作,操作有以下两种类型:
1 x c
:表示向队列中插入\(c\)个值为\(x\)的元素2 c
:表示从队列中取出\(c\)个元素并输出这c
个元素的值的和
数据范围:\(1 \leq Q \leq 2 \times 10^5\)。
思路:将每次插入当做一个整体存储在队列中,考虑到出队的数量可能小于当前队首的数量,所以使用双端队列维护即可。
时间复杂度:\(O(n)\)
参考代码:
void solve() {
int q;
cin >> q;
using PII = pair<int, int>;
deque<PII> deq;
while (q--) {
int op, x, c;
cin >> op;
if (op == 1) {
cin >> x >> c;
deq.push_back({ c , x });
}
else {
cin >> c;
long long res = 0;
while (true) {
auto [nums, val] = deq.front(); deq.pop_front();
if (nums < c) {
c -= nums;
res += 1ll * nums * val;
}
else {
nums -= c;
res += 1ll * c * val;
deq.push_front({ nums , val });
break;
}
}
cout << res << '\n';
}
}
return;
}
E - Max Min
题目描述:给你一个长度为\(n\)的数组\(A\),和两个整数\(x , y (x \leq y)\),问你有多少个区间\([L , R]\),满足区间的最大值为\(y\),最小值为\(x\)。
思路:首先考虑到值小于\(x\)或者大于\(y\)的位置一定不能对答案做出贡献,所以我们可以将数组分解成最少的不重叠子串,使得对于每一段中的元素都在区间\([x , y]\)内。现在讨论其中的一个子串,假设这个子串的长度为\(m\),我们使用双指针,统计出现的数字,若\(x , y\)在区间\([lr , rs]\)内恰好都出现, 那么此时对答案的贡献为\(m - rs + 1\),然后我们移动左指针直到某一个临界值不在区间内,再去移动右指针,重复即可。
时间复杂度:\(O(n)\)
双倍经验: 992. K 个不同整数的子数组
参考代码:
void solve() {
int n, a, x, y;
cin >> n >> y >> x;
vector<int> nums;
long long res = 0;
auto cal = [&](){
int m = nums.size();
if (m == 1) return;
int rs = 1, cntx = nums[1] == x, cnty = nums[1] == y;
for (int lr = 1; lr < m; ++lr) {
while (rs < m && (cntx == 0 || cnty == 0)) {
if (++rs >= m) break;
cntx += nums[rs] == x;
cnty += nums[rs] == y;
}
res += m - rs;
cntx -= nums[lr] == x;
cnty -= nums[lr] == y;
}
return;
};
nums.push_back(0);
for (int i = 1; i <= n; ++i) {
cin >> a;
if (a >= x && a <= y) nums.push_back(a);
else {
cal();
nums.clear();
nums.push_back(0);
}
}
cal();
cout << res << '\n';
return;
}
双倍经验的参考代码:
class Solution {
public:
int subarraysWithKDistinct(vector<int>& nums, int k) {
int res = 0, n = nums.size();
vector<int>cnt(n + 1 , 0);
++cnt[nums[0]];
int lr = 0 , rs = 0, ct = 1;
for(int i = 0 ; i < n ; ++i){
while(lr < n && ct < k){
if(++lr >= n) break;
if(++cnt[nums[lr]] == 1) ++ct;
}
if(rs <= lr) rs = lr + 1;
while(rs < n && cnt[nums[rs]] != 0) ++rs;
if(lr < n) res += rs - lr;
if(--cnt[nums[i]] == 0) --ct;
}
return res;
}
};
F - Cards
题目描述:给你\(n\)张卡片,卡片的正面的数字集合是一个\(n\)的排列,卡片的背面的数字集合也是一个\(n\)的排列,问你有多少种选择方案可以使得选出来的卡片包含有\(1 \sim n\)的所有数字,答案对998244353
取模。
思路:考虑到卡片正反面都是排列,若将数字\(i\)抽象成编号为\(i\)的顶点,对于一张卡片上的数字,假设正面为\(u\),背面为\(v\),那么我们假定顶点对\((u , v)\)之间存在一条有向边\(u\to v\),那么按照这样就可以构建成一个有向图,考虑到是排列,那么这个有向图就是由一个又一个不相交的环所组成。对于一个环上的数字,任意相邻两个就是一张卡片,那么要表示这个环所对应的数字集合,假设这个环上有\(m\)个顶点,即求:对于任意两个相邻的顶点必须选择其中一个的方案数。假设共有\(k\)个环,第\(i\)个环的顶点数为\(V_i\),设其对答案的贡献为\(g(V_i)\),根据乘法原理最终答案为:
时间复杂度:\(O(n)\)
参考代码:
const int mod = 998244353;
void solve() {
int n;
cin >> n;
vector<vector<int>>f1(n + 1, vector<int>(2, 0));//选择第一个
vector<vector<int>>f2(n + 1, vector<int>(2, 0));//不选第一个
f1[1][1] = 1;
f2[1][0] = 1;
for (int i = 2; i <= n; ++i) {
f1[i][0] = f1[i - 1][1];
f1[i][1] = (f1[i - 1][0] + f1[i - 1][1]) % mod;
f2[i][0] = f2[i - 1][1];
f2[i][1] = (f2[i - 1][0] + f2[i - 1][1]) % mod;
}
auto calCircle = [&](int m)->int {
if (m == 1) return 1;
return (1ll * f1[m][1] + f1[m][0] + f2[m][1]) % mod;
};
vector<int>adj(n + 1), a(n + 1), b(n + 1);
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) {
cin >> b[i];
adj[a[i]] = b[i];
}
vector<bool>vis(n + 1, false);
auto dfs = [&](auto&& dfs, int rt, int u)->int {
vis[u] = true;
if (u == rt) return 1;
return dfs(dfs, rt, adj[u]) + 1;
};
int res = 1;
for (int i = 1; i <= n; ++i) {
if (vis[a[i]]) continue;
int j = dfs(dfs , a[i], b[i]);
res = 1ll * res * calCircle(j) % mod;
}
cout << res << '\n';
return;
}
作者:cherish.
出处:https://home.cnblogs.com/u/cherish-/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。