牛客小白月赛101
A-tb的区间问题
思路
前缀和
我的解法蠢了,就是做一个前缀和和一个后缀和,枚举前i个和后k-i个,取一个max值就是答案。
但其实这题不用这么麻烦,不管删除第一个还是最后一个,最后都会留下一组连续的大小为n-k的子数组,所以我们只需要做一个前缀和,从第n-k开始,每次作为结尾向前取n-k个数,取一个max值,就是答案。
代码
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 5e3 + 10;
int n, k;
ll s[N];
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; i ++)
{
int x;
cin >> x;
s[i] = s[i - 1] + x;
}
ll res = 0;
for (int i = n - k; i <= n; i ++)
res = max(res, s[i] - s[i - n + k]);
cout << res;
return 0;
}
B-tb的字符串问题
思路
单调栈的板子题吧
让每个字符进栈,如果栈顶的两个字符组成为目标字符串就出栈两个字符,同时答案res++。
代码
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
string s;
cin >> s;
vector<char> st;
for (int i = 0; i < n; i ++)
{
st.push_back(s[i]);
if (st.size() > 1) {
if (st[st.size() - 2] == 'f' && st[st.size() - 1] == 'c' || st[st.size() - 2] == 't' && st[st.size() - 1] == 'b')
{
st.pop_back();
st.pop_back();
}
}
}
cout << st.size();
return 0;
}
C-tb的路径问题
思路
打表找规律
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2
1 1 3 1 1 3 1 1 3 1 1 3 1 1 3 1 1 3 1 1
1 2 1 4 1 2 1 4 1 2 1 4 1 2 1 4 1 2 1 4
1 1 1 1 5 1 1 1 1 5 1 1 1 1 5 1 1 1 1 5
1 2 3 2 1 6 1 2 3 2 1 6 1 2 3 2 1 6 1 2
1 1 1 1 1 1 7 1 1 1 1 1 1 7 1 1 1 1 1 1
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 8 1 2 1 4
1 1 3 1 1 3 1 1 9 1 1 3 1 1 3 1 1 9 1 1
1 2 1 2 5 2 1 2 1 10 1 2 1 2 5 2 1 2 1 10
1 1 1 1 1 1 1 1 1 1 11 1 1 1 1 1 1 1 1 1
1 2 3 4 1 6 1 4 3 2 1 12 1 2 3 4 1 6 1 4
1 1 1 1 1 1 1 1 1 1 1 1 13 1 1 1 1 1 1 1
1 2 1 2 1 2 7 2 1 2 1 2 1 14 1 2 1 2 1 2
1 1 3 1 5 3 1 1 3 5 1 3 1 1 15 1 1 3 1 5
1 2 1 4 1 2 1 8 1 2 1 4 1 2 1 16 1 2 1 4
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 17 1 1 1
1 2 3 2 1 6 1 2 9 2 1 6 1 2 3 2 1 18 1 2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 19 1
1 2 1 4 5 2 1 4 1 10 1 4 1 2 5 4 1 2 1 20
打了一张20*20的图表,你就会发现
- n = 1时,直接到达
- n = 2时,需要2步
- n = 3时,需要4步
- n > 3时,分两个情况,偶数和奇数。偶数的情况,可以发现每个大于2的偶数左边都有一个2,可以通过2不消耗能量一步到达偶数的左边再消耗两步到达n;奇数的情况,同样通过2来一步到达奇数的左上,再消耗4步到达。
代码
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
if(n == 1) cout << 0;
else if(n == 2) cout << 2;
else if(n == 3) cout << 4;
else {
if(n % 2 == 0) cout << 4;
else cout << 6;
}
return 0;
}
D-tb的平方问题
思路
前缀和+差分
先进行前缀和,因为题目数据范围不大,直接暴力枚举区间,如果区间和为完全平方数就把该区间标记+1作差分,最后再前缀和一遍。
代码
#include <iostream>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n, q;
ll a[N], s[N];
ll t[N];
bool is_(ll x)
{
if (x == 0) return false;
ll y = sqrt(x);
if (y * y == x) return true;
return false;
}
int main()
{
cin >> n >> q;
for(int i = 1 ; i <= n ; i ++)
{
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
for(int i = 1 ; i <= n ; i ++)
{
for(int j = 0 ; j < i ; j ++)
{
ll num = s[i] - s[j];
if(is_(num)) t[j + 1] ++ , t[i + 1] --;
}
}
for(int i = 1 ; i <= n ; i ++) t[i] += t[i - 1];
while (q --)
{
int x;
cin >> x;
cout << t[x] <<'\n';
}
return 0;
}
E-tb的数数问题
思路
官方给了一个线性筛筛因子数的知识点(又学到了)。
根据题意,凡是好数,它的所有因子都要在集合里,将集合里的数去重然后标记一下,再将不在集合里的数枚举,将它的所有倍数都筛掉,因为它不属于好数,它的倍数也不属于好数了,这样的话,我们只需枚举1~1e6,同时满足是集合里的数和没被筛掉的数两个性质就是好数。
代码
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
int n;
bool a[N], b[N];
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++)
{
int x;
cin >> x;
a[x] = 1;
}
for (int i = 1; i < N; i ++)
if (!a[i])
for (int j = i; j < N; j += i) b[j] = 1;
int res = 0;
for (int i = 1; i < N; i ++)
if (a[i] && !b[i]) res ++;
cout << res;
return 0;
}
F-tb的排列问题
思路
不会,以下是官方题解。
设 \(A\) 中未出现的数的集合为 \(P\)。
枚举第 \(i\) 次操作,此时,对于 \(B\) 排列,我们有两种情况:
-
若 \(b_i\) 不属于 \(P\): 此时 \(b_i\) 所对应的数如果在 \(A\) 中的初始位置就大于了 \(i + s\),则一定不存在操作使之复位,否则,其初始位置如果在 \(1\) 到 \(i - 1\),则一定通过置换操作移动到了 \(i\) 到 \(i + s\),则此次置换操作一定可以将其复位,能复位贡献为1,否则贡献为0。
-
若 \(b_i\) 属于 \(P\): 此时 \(b_i\) 可以填入 \(1\) 到 \(i + s\) 中所有为 \(-1\) 的位置,因为通过之前的操作,\(1\) 到 \(i - 1\) 的位置均已复位,则之前未填的为 \(-1\) 的位置通过置换操作至多移到了 \(i - 1 + s\) 的位置,于是一定可以填充,此时贡献为 \(sum(i + s) - {cnts}\),\({cnts}\) 为已经填充的 \(-1\) 个数,\(sum(i + s)\) 为 \(A\) 数组中下标为 \(1\) 到 \(i + s\) 中 \(-1\) 的个数;
最终答案为 \(1\),\(2\) 情况的所有贡献求积即可。
代码
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int N = 2e5 + 10;
ll a[N], b[N], pre[N];
void solve()
{
int n, w;
cin >> n >> w;
map<int, int> mp;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
mp[a[i]] = i;
pre[i] = pre[i - 1] + (a[i] == -1);
}
for (int i = 1; i <= n; i ++) cin >> b[i];
ll res = 1, cnt = 0;
for (int i = 1; i <= n; i ++)
{
if (mp.count(b[i])) {
if (mp[b[i]] > i + w) res = 0;
}
else {
res = res * (pre[min(i + w, n)] - cnt) % mod;
cnt ++;
}
}
cout << res << '\n';
}
int main()
{
int t;
cin >> t;
while (t --) solve();
return 0;
}