1、双端队列/单调队列 O(n)
int n,k,a[maxn],b[maxn],deq[maxn];//a[]保存读入的数组;b[]保存一段区间的答案;deq[]双端队列,保存数组下标
void upper()//值按照升序存放在队列中
{
int s=0,t=0;//s:首;t:尾
for(int i=0;i<n;i++)
{
while(s<t&&a[deq[t-1]]>=a[i]) t--;//进队就保证了顺序,弹出比他大的,进入小的;
deq[t++]=i;
if(i-k+1>=0)
{
b[i-k+1]=a[deq[s]];//首元素下标用不到就删除
if(deq[s]==i-k+1)
{
s++;
}
}
}
for(int i=0;i<(n-k);i++)//输出最终结果
{
printf("%d ",b[i]);
}
printf("%d\n",b[n-k]);
}
2、RMQ线段树 O(log(n))
const int M=INTMAX;
int N,dat[2*M-1];//N:树叶个数;
void init(int m)//线段树初始化
{
N=1;
while(N<m) N*=2;//构建一棵完整的二叉树,树叶为偶数个
for(int i=0;i<2*N-1;i++)
{
dat[i]=INTMAX;//初始化为最大值
}
}
void update(int k,int a)//向上更新节点
{
k+=N-1;
dat[k]=a;
while(k>0)
{
k=(k-1)/2;
dat[k]=min(dat[k*2+1],dat[k*2+2]);
}
}
int query(int a,int b, int k,int l,int r)//查询,[a,b):被查询区间;k:节点编号;[l,r):被比较区间
{ //主函数调用时,k=0,l=0,r=N
if(r<=a||b<=l) return INTMAX;//区间不相交
if(a<=l&&b>=r) return dat[k];//区间全覆盖
else//区间有部分交叉,返回左右区间中小的那个值
{
int v1=query(a,b,k*2+1,l,(l+r)/2);
int v2=query(a,b,k*2+2,(l+r)/2,r);
return min(v1,v2);
}
}
3、一维树状数组 (BIT) O(log(n))
int bit[MAXN+1];//树的所有节点
int sum(int i)//[1,i]区间的和
{
int s=0;
while(i>0)
{
s+=bit[i];
i-=i&(-i);
}
return s;
}
void add(int i,int x)//某个节点增加x,更新全部相关区间直到maxn节点;
{
while(i<MAXN)
{
bit[i]+=x;
i+=i&(-i);
}
}
4、二维树状数组
int bit[MAXN][MAXN];//树状数组大小选取与普通数组一样
int lowbit(int x)
{
return x&(-x);
}
int sum(int x,int y)
{
int s=0;
while(x>0)
{
for(int i=y;i>0;i-=lowbit(i))//行与列都是一维BIT,具体分析见之前转载的文章
{
s+=bit[x][i];
}
x-=lowbit(x);
}
return s;
}
void add(int x,int y,int num)
{
while(x<MAXN)
{
for(int i=y;i<MAXN;i+=lowbit(i))
{
bit[x][i]+=num;
}
x+=lowbit(x);
}
}
int query(int x1,int y1,int x2,int y2)
{
int ans=sum(x2,y2)+sum(x1-1,y1-1)-sum(x1-1,y2)-sum(x2,y1-1);
return ans;
}
5、线段树区间同加值、求和 O(log(n))
ll N,data[4*maxn],datb[4*maxn];
//开4倍数组,a:data[]:给该点对应的区间内所有元素共同加上的值
//b:datb[]:在该节点对应的区间中除去a以外其他值的和
void init(int m)
{
N=1;
while(N<m) N*=2;
for(int i=0;i<2*N+1;i++)
{
data[i]=0;
datb[i]=0;
}
}
void add(ll a,ll b,ll x,ll k,ll l,ll r)
{
if(a<=l&&b>=r) data[k]+=x;
else if(l<b&&a<r)
{
datb[k]+=(min(b,r)-max(a,l))*x;
add(a,b,x,k*2+1,l,(l+r)/2);
add(a,b,x,k*2+2,(l+r)/2,r);
}
}
ll sum(ll a,ll b,ll k,ll l,ll r)
{
if(b<=l||r<=a)
{
return 0;
}
else if(a<=l&&r<=b)
{
return data[k]*(r-l)+datb[k];
}
else
{
ll res =(min(b,r)-max(a,l))*data[k];
res+=sum(a,b,k*2+1,l,(l+r)/2);
res+=sum(a,b,k*2+2,(l+r)/2,r);
return res;
}
}
6、线段树区间修改为某值,求和 O(log(n))
int N,_sum;//次模板左闭右闭区间,且下标从1开始;具体见算法竞赛入门经典
int setv[4*maxn],sumv[4*maxn];
void init(int m)
{
N=1;
while(N<m) N*=2;
for(int i=1;i<2*N+1;i++)
{
setv[i]=0;//初始标记根据题目数字来改动
sumv[i]=0;
}
}
void maintain(int o,int a,int b)
{
int lc=o*2;
int rc=o*2+1;
sumv[o]=0;
if(setv[o]>0)
{
sumv[o]=setv[o]*(b-a+1);
}
else if(b>a) sumv[o]=sumv[lc]+sumv[rc];
}
void pushdown(int o)
{
int lc=o*2;
int rc=o*2+1;
if(setv[o]>0)
{
setv[lc]=setv[rc]=setv[o];
setv[o]=0;
}
}
void update(int a,int b,int v,int o,int l, int r)
{
int lc=o*2;
int rc=o*2+1;
if(a<=l&&b>=r)
{
setv[o]=v;
}
else
{
pushdown(o);
int M=(r+l)/2;
if(a<=M) update(a,b,v,lc,l,M);
else maintain(lc,l,M);
if(b>M) update(a,b,v,rc,M+1,r);
else maintain(rc,M+1,r);
}
maintain(o,l,r);
}
void query(int a,int b,int o,int l,int r)
{
if(setv[o]>0)
{
_sum+=setv[o]*(min(b,r)-max(l,a)+1);
}
else if(a<=l&&b>=r)
{
_sum+=sumv[o];
}
else
{
int M=(r+l)/2;
if(a<=M) query(a,b,o*2,l,M);
if(b>M) query(a,b,o*2+1,M+1,r);
}
}
另见:https://www.cnblogs.com/Kiraa/p/6046208.html(感谢!)
https://blog.csdn.net/Fire_to_cheat_/article/details/80713972
7、优先队列
# include<queue>
struct node
{
int h;
int st;
friend bool operator < (node a,node b)
{
return a.st>b.st;//它也是按照重载后的小于规则,从大到小排序的。入队后自动排序
}
};
priority_queue<node> Q;
Q.push();
Q.top();
Q.pop();
8.1、凸包算法(O(nlogn))
struct node
{
ll x,y;
};
node n[maxn],