各个算法模板大全
YclarHIM0302 · · 个人记录
各个算法模板大全
(收集中)
1. 线段树
注释
l 表示区间左端点,r表示区间右端点,s表示线段树左端点,t表示线段树右端点,p表示当时所在点的下标。
注意
线段树一定要开4倍内存!
建树操作
void build(int s,int t,int p)
{
if(s==t)
{
d[p]=a[s];//a是输入数组
return;
}
int m=s+((t-s)>>1);
build(s,m,p*2),build(m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1];
}
带懒惰标记的区间更新(加法)
void update(int l,int r,int c,int s,int t,int p)
{
if(l<=s&&t<=r)
{
d[p]+=(t-s+1)*c,b[p]+=c;
return;
}
int m=s+((t-s)>>1);
if(b[p]&&s!=t)
{
d[p*2]+=b[p]*(m-s+1),d[p*2+1]+=b[p]*(t-m);
b[p*2]+=b[p],b[p*2+1]+=b[p];
b[p]=0;
}
if(l<=m) update(l,r,c,s,m,p*2);
if(r>m) update(l,r,c,m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1];
}
不带懒惰标记的区间查询
int getsum(int l,int r,int s,int t,int p)
{
if(l<=s&&t<=r) return d[p];
int m=s+((t-s)>>1),sum=0;
if(l<=m) sum+=getsum(l,r,s,m,p*2);
if(r>m) sum+=getsum(l,r,m+1,t,p*2+1);
return sum;
}
带懒惰标记的区间查询
int getsum1(int l,int r,int s,int t,int p)
{
if(l<=s&&t<=r) return d[p];
int m=s+((t-s)>>1),sum=0;
if(b[p]&&s!=t)
{
d[p*2]+=b[p]*(m-s+1),d[p*2+1]+=b[p]*(t-m);
b[p*2]+=b[p],b[p*2+1]+=b[p];
b[p]=0;
}
if(l<=m) sum+=getsum(l,r,s,m,p*2);
if(r>m) sum+=getsum(l,r,m+1,t,p*2+1);
return sum;
}
带懒惰标记的区间修改
void update_change(int l,int r,int c,int s,int t,int p)
{
if(l<=s&&t<=r)
{
d[p]=(t-s+1)*c,b[p]=c;
return;
}
int m=s+((t-s)>>1);
if(b[p]&&s!=t)
{
d[p*2]=b[p]*(m-s+1),d[p*2+1]=b[p]*(t-m);
b[p*2]=b[p],b[p*2+1]=b[p];
b[p]=0;
}
if(l<=m) update(l,r,c,s,m,p*2);
if(r>m) update(l,r,c,m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1];
}
带懒惰标记的区间更新(加法+乘法)
void pushdown(int s,int t,int p)//mod是取余数
{
int m=s+((t-s)>>1);
d[2*p]=(d[2*p]*b1[p]+(m-s+1)*b[p])%mod;
d[2*p+1]=(d[2*p+1]*b1[p]+(t-m)*b[p])%mod;
b1[2*p]=(b1[2*p]*b1[p])%mod;
b1[2*p+1]=(b1[2*p+1]*b1[p])%mod;
b[2*p]=(b[p*2]*b1[p]+b[p])%mod;
b[2*p+1]=(b[2*p+1]*b1[p]+b[p])%mod;
b1[p]=1;
b[p]=0;
}
void add(int l,int r,long long c,int s,int t,int p)
{
if(l<=s&&t<=r)
{
d[p]=(d[p]+(t-s+1)*c%mod)%mod,b[p]=(b[p]+c%mod)%mod;
return;
}
int m=s+((t-s)>>1);
pushdown(s,t,p);
if(l<=m) add(l,r,c,s,m,p*2);
if(r>m) add(l,r,c,m+1,t,p*2+1);
d[p]=(d[p*2]+d[p*2+1])%mod;
}
void multi(int l,int r,long long c,int s,int t,int p)
{
if(l<=s&&t<=r)
{
d[p]=(d[p]*c)%mod;
b1[p]=(b1[p]*c)%mod;
b[p]=(b[p]*c)%mod;
return;
}
int m=s+((t-s)>>1);
pushdown(s,t,p);
if(l<=m) multi(l,r,c,s,m,p*2);
if(r>m) multi(l,r,c,m+1,t,p*2+1);
d[p]=(d[p*2]+d[p*2+1])%mod;
}
练习题
模板Ⅰ
模板Ⅱ
2.树状数组
基本核心:lowbit函数
基本定义:
一个数的二进制表达式中最低位的1以及其后的0组成的数所对应的值。
e.g.
图表示例
如图所示,d[2]所储存的值是d[1]+a[2],且d[2]的lowbit值为2。
int lowbit(int x)
{
return x&-x;
}
修改单个结点值(加法)
void add(int x,int k)
{
while(x<=n)//n为最右点
{
c[x]+=k;
x+=lowbit(x);
}
}
查询第1~x项结点值之和
int getsum(int x)
{
int ans=0;
while(x>=1)
{
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
差分数组
如图所示,差分数组b每项的值是a数组每项的差值。其公式为:
在修改一个区间的结点值时,就可以使用差分数组进行运算。
e.g.
| 下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| a | 0 | 1 | 2 | 4 | 1 |
| b | 0 | 1 | 1 | 2 | -3 |
要求[1,3]区间数值加一:
| 下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| a | 0 | 1+1 | 2+1 | 4+1 | 1 |
| b | 0 | 1+1 | 1 | 2 | -3-1 |
发现了吗?只有b[1]与b[4]的值变化了。
结论:当区间[l,r]加上一个值k时:
修改区间结点值(加法,差分数组)
void _add(int x,int k)
{
int v=x*k;
while(k<=n)
{
t1[x]+=k;
t2[x]+=v;//(i-1)*b_i数组
x+=lowbit(x);
}
}
void add1(int l,int r,int v)//使用时调用此处
{
_add(l,v);
_add(r+1,-v);
}
查询区间结点值之和(差分数组)
int _getsum(int *t,int x)
{
int ans=0;
while(x)
{
ans+=t[x];
x-=lowbit(x);
}
return ans;
}
long long getsum1(int l,int r)
{
return(r+1ll)*_getsum(t1,r)-1ll*l*_getsum(t1,l-1)-(_getsum(t2,r)-_getsum(t2,l-1));
}
练习题
模板Ⅰ
模板Ⅱ