前缀和与差分
激光炸弹
预处理 使n^4 变成n^2
二位前缀和
#include <iostream>
using namespace std;
const int maxn = 5e3 + 5;
int sum[maxn][maxn];
int main(){
int n, r;
cin >> n >> r;
for(int i = 1, x, y, c; i <= n; i++ ) {
cin >> x >> y >> c;
x ++ ,y ++;
sum[x][y] += c;
}
for(int i = 1; i < maxn; i ++ )
for(int j = 1; j < maxn ; j ++ )
sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
int ans = 0;
for(int i = r; i < maxn; i ++ ) for(int j = r; j < maxn; j ++ )
ans = max(ans, sum[i][j] + sum[i - r][j - r] - sum[i - r][j] - sum[i][j - r]);
cout << ans << endl;
return 0;
}
IncDec序列
P22 一共3种有效改变序列的方法
有限正负抵消 只剩下 正数和负数的时候 让他和1 or n + 1 进行改变
这样 序列最后有 正数和P 负数asb和 Q ABS(P-Q) + 1 种情况
最少操作是 min(P, Q) + abs(P - Q);
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5 ;
int n, m;
int a[maxn], b[maxn];
int main() {
cin >> n;
ll x = 0, y = 0;
for(int i = 1; i <= n; i++) {
cin >> a[i];
if(i == 1) b[1] = a[1];
else {
b[i] = a[i] - a[i - 1];
if(b[i] > 0) x += b[i];
else y -= b[i];
}
}
cout << max(x, y) << endl << abs(x - y) + 1 << endl;
return 0;
}
最高的牛
A可以看到B 意味A B之间的数据都小于他们
#include <iostream>
#include <map>
using namespace std;
typedef pair<int,int> P;
const int maxn = 100000 + 5 ;
int b[maxn];
map<P,bool> vis;
int main() {
int n, i, h, R;
cin >> n >> i >> h >> R;
int x, y;
for(int i = 1 ; i <= R ; i++) {
cin >> x >> y;
if(x > y) swap(x, y);
if(vis[P(x,y)]) continue;
vis[P(x,y)] = 1;
b[x + 1]--, b[y] ++;
}
for(int i = 1 ; i<=n ; i++){
b[i]+=b[i-1];
cout << h + b[i] << endl;
}
return 0;
}
二分
整数域上的二分
- 当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。
- 当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。
- 当我们要找的解集落在 l 到 mid 之间时选 1方法 ; 反之 选2方法
实数域上的二分
一般确定好eps 通常比要求 +2 即可
或者 for个100次什么的
ps 补充 后面的hash 找回文串 树状数组 找k大 最短路上的 k值路经过问题 都可以 转到二分判定上
三分求单峰函数极值 链接: https://blog.csdn.net/qq_40831340/article/details/90639163
最佳牛围栏
找一个长度大于L 连续的子段 同时这一段 平均数最大
1 首先有无长度现在的字段和最大问题 这个只要贪心一直加 加到<0 就清空重新加 找期间出现的最大值
2 子段长度不小于L 找他最大区段和
子段和可以变成前缀和减法的形式 这样 我们便可以 on的处理
同时 引入一个 生存周期 的变量 去存 sumr - suml r之前出现的最小 suml 这样就能 直接减到最小值 找到最大值
如此以来 我们可以考虑二分平均值 看看这个序列每一位 - 平均值能不能搞出来 整体大于0的数据
这样就有单调性了
#include <iostream>
using namespace std;
const int maxn = 1e5 + 5 ;
const double eps = 1e-6;
int n, L;
double a[maxn], b[maxn];
double sum[maxn];
bool chk(double mid) {
for(int i = 1; i <= n; i++)
b[i] = a[i] - mid;
for(int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + b[i];
double min_val = 1e10, ans = -1e10;
for(int i = L ; i <= n; i++) {
min_val = min(min_val, sum[i-L]);
ans = max(ans, sum[i] - min_val);
}
return ans >= 0;
}
int main() {
cin >> n >> L;
for(int i = 1; i <= n; i++)
cin >> a[i];
double l = 0, r = 1e8;
while(r-l>=eps) {
double mid = (l + r) / 2;
if(chk(mid))
l = mid;
else
r = mid;
}
cout << (int)(r * 1000) << endl;
return 0;
}
特殊排序
虽然是N^2 吧 题目要求不能cmp 超过1000次 所以二分找位置
class Solution {
public:
vector<int> specialSort(int N) {
vector<int> res;
res.push_back(1);
for(int i=2;i<=N;i++){
int l=0,r=res.size();
while(l<r){
int mid=(l+r)>>1;
if(compare(res[mid],i)) l=mid+1;
else r=mid;
}
res.push_back(i);
for(int j=res.size()-1;j>l;j--) swap(res[j],res[j-1]);
}
return res;
}
};