模板1
A - 基本算法。
快速幂
ll qpow(ll a,int b){
ll res=1ll;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;b>>=1;
}
return res;
}
B - 数据结构。
树状数组
单点加,区间查:
//BIT
ll c[N];
int tot;
void ins(int x,ll v){//x!=0
for(;x<=tot;x+=x&(-x)) c[x]+=v;
}
ll qry(int x){//x 可以 =0
ll res=0;
for(;x;x-=x&(-x)) res+=c[x];
return res;
}
求全局第 k 小:
int kth(int k){
int p=1<<_log;//_log=ceil(log n)
for(int i=_log-1;~i;i--){
int lc=p-(1<<i);
if(k<=c[lc]) p=lc;//走左子
else k-=c[lc];//左端点右移到 lc+1
}
}
线段树
标记永久化:
修改:
-
在途径的每个区间 sum 加上 w 会产生的贡献:w 与交集大小的乘积。
-
碰到被修改区间完全覆盖的区间时打上 add 标记,说明 w 作用于该区间每个位置。
查询:
-
被完全覆盖区间只贡献 sum。
-
沿路的非完全覆盖区间 add 都贡献给 当前区间与查询区间的交集。
用途:
-
动态开点线段树如果不想下放懒标记时把空儿子建出来。
//permanent tag
const int pd=N<<2;//4N
ll sum[pd],add[pd];
void cg(int s,int e,ll w,int k=1,int l=1,int r=n){
sum[k]+=(min(r,e)-max(l,s)+1)*w;
if(s<=l&&r<=e) return add[k]+=w,void();
if(s<=mid) cg(s,e,w,lc,l,mid);
if(e>mid) cg(s,e,w,rc,mid+1,r);
}
ll qry(int s,int e,int k=1,int l=1,int r=n){
if(s<=l&&r<=e) return sum[k];
ll cur=(min(r,e)-max(l,s)+1)*add[k];
if(s<=mid) cur+=qry(s,e,lc,l,mid);
if(e>mid) cur+=qry(s,e,rc,mid+1,r);
return cur;
}
动态开点线段树:
空间 2N。
//dynamic building SGT
