线段树:
线段树或前缀树都可以来求区间和
线段树:log(n),适用于修改
使用懒标记用作修改
题目描述
给定一个 n 位数组和两种操作:
操作1:数组中某个区间的所有数字加上一个值
操作2:查询数组中某个区间的所有数字之和
输入
第一行输入两个整数 n,m(1≤n≤10000,3≤m≤100000),分别代表数组大小和操作数。
第二行包含 n 个整数,代表数组中相应的数字,数字大小不会超过 int 表示范围。
接下来 m 行,每行三个或四个整数 a,b,c,d(a∈[1,2])
当 a=1 时,接下来输入 b,c,d,代表将数组 [b,c]区间内的数字加上 d,(1≤b,c≤n, d is int),当 b>c 时,不做任何操作。
当 a=2 时,接下来输入 b,c,输入代表询问 [b,c] 区间内的和值 ( 1≤b,c≤n),当 b>c 时,输出 0。
输出
对于每个 a=2 的操作,输出查询区间内数字的和值,答案不会超过64位整型(long long)的表示范围。
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
int l,r,cnt;
long long sum,lazy;
};
node tree[40005];
int n,m;
long long num[10005];
void up_sum(int now){
tree[now].sum=tree[now*2].sum+tree[now*2+1].sum;
}
void down_lazy(int now){
if(tree[now].lazy!=0){
tree[now*2].lazy+=tree[now].lazy;
tree[now*2].sum+=tree[now*2].cnt*tree[now].lazy;
tree[now*2+1].lazy+=tree[now].lazy;
tree[now*2+1].sum+=tree[now*2+1].cnt*tree[now].lazy;
tree[now].lazy=0;
}
}
void built_tree(int now,int l,int r){
tree[now].l=l;
tree[now].r=r;
tree[now].cnt=r-l+1;
tree[now].lazy=0;
if(l==r){
tree[now].sum=num[l];
return ;
}
int mid=(l+r)/2;
built_tree(now*2,l,mid);
built_tree(now*2+1,mid+1,r);
up_sum(now);
}
void modify(int now,int l,int r,int v){
if(tree[now].l>=l&&tree[now].r<=r){
tree[now].sum+=tree[now].cnt*v;
tree[now].lazy+=v;
return ;
}
down_lazy(now);
int mid=(tree[now].l+tree[now].r)/2;
if(mid>=l)modify(now*2,l,r,v);
if(mid<r)modify(now*2+1,l,r,v);
up_sum(now);
}
long long query(int now,int l,int r){
if(tree[now].l>=l&&tree[now].r<=r){
return tree[now].sum;
}
down_lazy(now);
int mid=(tree[now].l+tree[now].r)/2;
long long t=0;
if(mid>=l)t+=query(now*2,l,r);
if(mid<r)t+=query(now*2+1,l,r);
return t;
}
int main(){
cin>>n>>m;
for(int i=0;i<=n;i++){
cin>>num[i];
}
built_tree(1,1,n);
for(int i=0;i<m;i++){
int t,a,b,c;
cin>>t;
if(t==1){
cin>>a>>b>>c;
modify(1,a,b,c);
}else{
cin>>a>>b;
cout<<query(1,a,b)<<endl;
}
}
return 0;
}
单调栈(有序栈):
适合向前或向后大于小于某个数的问题
leetcode84. 柱状图中最大的矩形
class Solution {
public:
struct node{
int ind,h;
};
int largestRectangleArea(vector<int>& heights) {
stack<node>sta;
sta.push((node){-1,-1});
int ans=0;
for(int i=0;i<heights.size();i++){
while(sta.size()!=1&&sta.top().h>heights[i]){
node temp=sta.top();
sta.pop();
ans=max(ans,temp.h*(i-sta.top().ind-1));
}
sta.push(node{i,heights[i]});
}
while(sta.size()!=1){
node temp=sta.top();
sta.pop();
ans=max(ans,temp.h*((int)heights.size()-1-sta.top().ind));
}
return ans;
}
};
行人看楼问题:
从前往后求个单调递减栈
再从后往前求个单调递减栈
单调队列:
题目描述
给出一个长度为 N 的数组,一个长为 K 的滑动窗口从最左移动到最右,每次窗口移动,如下图:
找出窗口在各个位置时的极大值和极小值。
输入
第一行两个数 N,K。
第二行有 N 个数,表示数组中的元素。
输出
输出两行,第一行为窗口在各个位置时的极小值,第二行为窗口在各个位置时的极大值。
样例输入
8 3
1 3 -1 -3 5 3 6 7
样例输出
-1 -3 -3 -3 3 3
3 3 5 5 6 7
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{
int ind,val;
};
int n,k,num[300005],a1[300005],a2[300005];
int main(){
cin>>n>>k;
deque<node>mmin,mmax;
for(int i=0;i<=n;i++){
cin>>num[i];
while(!mmin.empty()&&mmin.back().val>num[i]){
mmin.pop_back();
}
mmin.push_back((node){i,num[i]});
if(mmin.front().ind+k<=i){
mmin.pop_front();
}
while(!mmax.empty()&&mmax.back().val<num[i]){
mmax.pop_back();
}
mmax.push_back((node){i,num[i]});
if(mmax.front().ind+k<=i){
mmax.pop_front();
}
if(i>=k){
a1[i]=mmin.front().val;
a2[i]=mmax.front().val;
}
}
for(int i=k;i<=n;i++){
if(i!=k)cout<<" ";
cout<<a1[i];
}
cout<<endl;
for(int i=k;i<=n;i++){
if(i!=k)cout<<" ";
cout<<a2[i];
}
cout<<endl;
return 0;
}