本场题目和题解由邱一凡学长和我一同给出。另附上邱一凡学长的博客:小白猿@
链接:删除字符
#include
#include
using namespace std;
const int N = 110;
char s[N];
int main()
{
int t;
cin >> (s + 1) >> t;
int n = strlen(s + 1);
string ans = "";
for (int i = 1; i <= n; i ++) {
char c = 'Z'; // 初始化最大
for (int j = 0; i + j <= n && j <= t; j ++ ) { // [i, i + t]段找最小字符,这些数才能通过删除最多t个字符到达前面去
c = min(c, s[i + j]);
}
int k = i; // 记录最小字符的下标
for (int j = 0; i + j <= n && j <= t; j ++ ) { // 找最小字符所在的下标
if (c == s[i + j]) {
k = i + j;
break;
}
}
ans += c;
t -= (k - i);
i = k;
}
for (int i = 0; i < ans.size() - t; i ++ ) {
cout << ans[i];
}
return 0;
}
题目好像需要vip了,暂时先看看题目和代码吧。
链接:美丽的区间
根据题意可得,答案具有两段性(即至少存在一个大于等于答案的长度一定是美丽区间,小于答案的长度一定不是美丽区间)。因此可以直接二分,对于二分出来的值 m i d mid mid, 如果存在一个i,使得区间 [ i − m i d + 1 , i ] [i - mid + 1, i] [i−mid+1,i] 的和大于等于 s s s,那么这个一定是一个美丽区间,返回此长度可行。
#include
using namespace std;
const int N = 1e5 + 10;
int sum[N]; // 前缀和数组
int n, s;
bool check(int mid)
{
for (int i = mid; i <= n; i ++ ) {
if (sum[i] - sum[i - mid] >= s) return true; // 前缀和O(1)得到区间[i, i - mid + 1]的和
}
return false;
}
int main()
{
cin >> n >> s;
for (int i = 1; i <= n; i ++ ) {
int x;
cin >> x;
sum[i] = sum[i - 1] + x; // 求前缀和
}
int l = 1, r = n + 1;
while (l < r) {
int mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid + 1;
}
if (l == n + 1) l = 0; // 说明整个区间的和都小于s,无解
cout << l << endl;
return 0;
}
#include
using namespace std;
const int N = 1e5 + 10;
int sum[N];
int n, s;
int main()
{
cin >> n >> s;
for (int i = 1; i <= n; i ++ ) {
int x;
cin >> x;
sum[i] = sum[i - 1] + x;
}
int ans = n + 1; // 定义一个如果存在合法的答案就会被替换的值
for (int l = 1, r = 1; l <= n; l ++ ) {
while (r < n && sum[r] - sum[l - 1] < s) r ++;
if (sum[r] - sum[l - 1] >= s) ans = min(ans, r - l + 1);
}
if (ans == n + 1) ans = 0; // 长度为n + 1的不存在
cout << ans << endl;
return 0;
}
链接:XOR Mixup
// XOR Mixup https://codeforces.com/problemset/problem/1698/A
/*
异或和性质:
1.a[i] ^ a[i] = a[i]
2.a[i] ^ 0 = a[i]
设sum = ai ^ a_{i + 1} ^ …… ^ a_n
我们求所有数的异或和sum后,用sum ^ ai 根据性质1,将原式中存在ai还原成0,再根据性质二就得到了
除ai以外所有数的异或和,判断是否和ai相同即可
*/
#include
using namespace std;
const int N = 110;
int n, a[N];
void solve(){
cin >> n;
int XOR_sum = 0;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
XOR_sum ^= a[i];
}
for (int i = 1; i <= n; i ++) {
if (XOR_sum ^ a[i] == a[i]) {
cout << a[i] << "\n";
return ;
}
}
}
int main(){
int t;
cin >> t;
while(t --){
solve();
}
return 0;
}
链接:P8809 [蓝桥杯 2022 国 C] 近似 GCD
#include
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 2e5 + 10;
int g[N];
void solve()
{
int n, gd;
cin >> n >> gd;
for (int i = 1; i <= n; i ++ ) cin >> g[i];
ll ans = 0;
int cnt = 0; // 区间里面不是g的倍数的个数
for (int l = 1, r = 0; l <= n; l ++ ) {
while (r < n && (!cnt || g[r + 1] % gd == 0)) { // 判断当前r + 1是否能进入到区间
r ++;
if (g[r] % gd != 0) cnt ++; // 不是g的倍数数量加一
}
if (g[l] % gd != 0) cnt --;
if (r - l + 1 >= 2) ans += r - l; // 不是g的倍数数量减一,因为g[l]下一次会从区间删除
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int __ = 1;
// cin >> __;
while (__ -- ) solve();
return 0;
}
对于每一个 a i a_i ai 动态维护两个数 l a s t 1 , l a s t 2 last1, last2 last1,last2,分别为从 i i i 开始向前的第一个和第二个 非 g g g 的倍数的下标,当区间内非 g g g 倍数的数的数量 < = 1 <=1 <=1 时该区间就是可行的,于是答案就是 a n s + = i − l a s t 2 − 1 ans += i - last2 - 1 ans+=i−last2−1。
// P8809 [蓝桥杯 2022 国 C] https://www.luogu.com.cn/problem/P8809
#include
using namespace std;
#define ll long long
const int N = 1e5 + 10;
int a[N];
inline int gcd(int a, int b){
if(b) while( (a %= b) && (b %= a));
return a + b;
}
int main(){
int n, g;
cin >> n >> g;
for (int i = 1; i <= n; i ++) cin >> a[i];
int last1 = 0, last2 = 0; // last1:从i向前数第一个非g的倍数下标,last2:第二个非g的倍数的下标
ll ans = 0;
for (int i = 1; i <= n; i ++) {
if(a[i] % g != 0) { // 更新
last2 = last1;
last1 = i;
}
ans += i - last2 - 1; // 我可以将last1所在的数改成g,那么从[j, i] (last2 + 1 <= j <= i)肯定都是合法的,-1是减去长度为1的区间
// 关于gcd是g的倍数情况,我们也可以将某一个数直接改为g达到要求
}
cout << ans;
return 0;
}
修改数组
转换为 有一个有所有正整数的集合,每次拿一个大于等于当前数的数出来,恰好符合二分每次找 > = >= >= 当前数的第一个数。每次找到后从 set 中删除即可。
//修改数组 https://www.lanqiao.cn/problems/185/learning/?page=8&first_category_id=1&sort=students_count
#include
using namespace std;
const int N = 1e6 + 10 + 1e5, MAX = 1100000;
set<int>s;
int main(){
int n;
cin >> n;
for (int i = 1; i <= MAX; i ++){
s.insert(i); // 将此时没有使用过的数存入set
}
for (int i = 1; i <= n; i ++) {
int x; cin >> x;
auto it = s.lower_bound(x); // 每次二分找大于等于当前ai的数
cout << (*it) << " ";
s.erase(it); // 将已经使用过的数丢弃
}
return 0;
}
//修改数组 https://www.lanqiao.cn/problems/185/learning/?page=8&first_category_id=1&sort=students_count
#include
using namespace std;
const int N = 1e6 + 100010, MAX = 1100000;
int p[N];
int get(int x){ // 压缩路径
if(x != p[x]) p[x] = get(p[x]);
return p[x];
}
int main(){
int n;
cin >> n;
for (int i = 1; i <= MAX; i ++) p[i] = i; // 并查集,每个数都指向大于等于自己的第一个数
for (int i = 1; i <= n; i ++) {
int x; cin >> x;
int r = get(x);
p[r] = r + 1; // 当前找到的数被使用,于是要指向该数的下一个
cout << r << " ";
}
return 0;
}