csp 202112-2 序列查询新解
题目口语化:
给一个有序严格递增序列A,n+1个,第一个定义为0,f(x) 表示A中小于等于x的最大下标
再给一个N 求 f(x)的和 x∈[0,N)
再给一个估算f(x)的g(x) r=floor(N/(n+1)) g(x)=floor(x/r);
求|g(x)-f(x)| 的和
思路:
如果模拟的话,首先算出 f(x) 需要o(N) 超出范围
对于A中的元素y,f(y)=y ,在A[i]与A[i+1]中间的f(x) 也相等
其次 从g(x)的求解公式中可以看出 g(x) 在长度为r区间内取值一致
所以对A中的元素进行遍历,在每个长度为r的区间内只计算一次g(x) 再计算 r * abs(f(x)-g(x))
如果区间超出了A[i+1] 则缩小dr 使其刚好 再对超出的一部分特殊化处理
问题等价 :上面一个序列的不固定长度区间的值相等 下面一个序列的固定长度的区间值相等 求两个序列每个位置的差的和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
ll n, N;
scanf("%lld %lld", &n, &N);
ll a[n + 2] = {0}; //多开 以防万一
a[n + 1] = N;
for (ll i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
ll sum = 0;
ll r = N / (n + 1);
ll gx = 0;
ll fx = 0;
ll dr = r;
ll ddr;
int flag = 0; //0表示没有超出 1超出
for (int i = 0; i <= n; i++) {
fx = i;
for (ll j = a[i]; j < a[i + 1]; j += dr) { //a[i]和a[i+1] 之间有多个dr
gx = j / r;
ll res = abs(gx - fx); // 0 2 5 8
if (flag == 1) { // 0123456789 A[i]>=i
dr = ddr; //这里处理超出的特例 0011122233
flag = 0; // 0011223344
} else {
dr = r;
}
if (j + dr <= a[i + 1]) { //等于的目的是j+dr 包含j 不包含j+dr
sum += dr * res;
} else { //有一段超出了
sum += (a[i + 1] - j) * res;
ddr = dr - (a[i + 1] - j); //ddr表示超出了多少个
flag = 1;
}
}
}
printf("%lld", sum);
return 0;
}
202109-2 非零段划分
问题口水话:
非P段划分 给一个序列A,再给一个p,A中凡是一个区间内的元素都大于p则为一个划分。求p,使得A大于p的划分个数最多
解法:
如果模拟,先对0~p循环,遍历A,计算个数 复杂度o(pn)<=510^9 不满足
用空间换时间:存下每个数的位置,类似字典
找规律:如果选中p,p的位置为i,若A[i-1]和A[i+1] 都大于p 则ans++;
若A[i-1]和A[i+1] 都小于p 则ans-- 其余不变;
#include <bits/stdc++.h>
using namespace std;
vector<int> pos[5000001];
int main() {
vector<int> a = {0};
set<int> b;
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
int j;
scanf("%d", &j);
a.push_back(j);
pos[j].push_back(i);
b.insert(j);
}
a.push_back(0);
int ans = 0;
//!做最坏的打算 序列就是123456n 排序并不能减少次数
//!怎么选p是关键 错错错
//!p=1 则全部变为0
//!p从小到大 如果结果下降 则为最大 区间里都比p大 如果一个区间灭亡 再曾大p就会更小 nonono 不一定
//p的选择不能再优化
//只能优化内层循环,用空间换时间,在输入的时候就存储位置信息
//优化判断非零段
//对p从小到大进行 如果改变的位置的前后均大于*p则ans++ 都小于p时ans-- 一大一小不变
// for (auto val : a) {
// cout << val << " " << endl;
// }
int m, base = 0;
for (m = 1; m <= n; m++) {
if (a[m] > 0 && a[m - 1] == 0) {
base++;
}
}
b.erase(0);
// cout << "ans is" << ans << endl;
int p = base;
for (auto val = b.begin(); val != b.end(); ++val) {
for (auto t : pos[*val]) { //这里要考虑比val都要小的pos
// cout << *val << "is" << t << endl;
// cout << a[t - 1] << " " << a[t + 1] << endl;
if (a[t - 1] > *val && a[t + 1] >= *val) { //后面的pos还没变成0
p++;
}
if (a[t - 1] <= *val && a[t + 1] < *val) {
p--;
}
//++迭代器 和 迭代器++
// for (auto va : pos[*val]) {
// cout << va << " ";
// }
// cout << endl << "第一次" << *(val) << endl;
//
// cout << "第二次" << *(val) << endl;
// cout << "p is" << p << endl;
}
ans = max(ans, p);
// cout << "ans is" << ans << endl;
// if (p > ans) {
// ans = p;
// cout << ans;
// }
}
// for (auto val : b) {
cout << val << endl;
// int p = 0, flag = 0, m;
// for (m = 0; m < n; m++) {
// if (a[m] > val - 1) {
// flag = 1;
// }
// if (a[m] <= val - 1 && flag == 1) {
// flag = 0;
// p++;
// }
//
// }
// if (a.back() >= val) {
// p++;
// }
// if (p > ans) {
// ans = p;
// }
// }
printf("%d", ans);
return 0;
}