根号算法
数论分块
luogu2261 余数求和
求$large sumlimits_{i=1}^nk%i $ 显然有(large n~\%~i=n-lfloorfrac n i floor) 提一个(n)
易证(largelfloorfrac a{bc} floor=lfloorfrac{lfloorfrac ab floor}c floor)
根据分块理论易知(largelfloorfrac nd floor)取值有(le2sqrt n)种
数论分块优化含(largelfloorfrac ni floor)的式子
设(large k=lfloorfrac ni floor),当(largelfloorfrac nj floor=k)时,(large j_{max}=lfloorfrac nk floor)
以(large[i,j])为一块,分块求和
假设一个块其实位置是(i),这个块的范围(large[i,lfloorfrac n {lfloorfrac ni floor} floor])
特判除法除(0)
long long ans = n * k;
for (long long l = 1, r; l <= n;l = r + 1){
//此处l意同i,r意同j,下个计算区间的l应为上个区间的r+1
if (k / l != 0)
r = min(k / (k / l), n);
else
r = n; // l大于k时
ans -= (k / l) * (r - l + 1) * (l + r) / 2;
//这个区间内k/i均相等,对i求和是等差数列求和
}
分块
入门1 区间加,单点查
const int N = 5e4 +5;
int a[N],b[230];
int main(){
int n,block,op,l,r,c;
scanf("%d",&n);
block = sqrt(n/3);
for(int i = 0;i < n;++i) scanf("%d",&a[i]);
for(int i = 0;i < n;++i){
scanf("%d%d%d%d",&op,&l,&r,&c);
--l; --r;//下标为0
if(op) printf("%d
",a[r] + b[r/block]);
else{
for(;l <= r && l % block;++l) a[l] = c;//改左
for(;l+block-1 <= r;l += block) b[l/block] += c;//整块
for(;l <= r;++l) a[l] += c;
}
}
}
入门2
区间加法,询问区间内小于某个值的个数
#define resort(P) {memcpy(b + P,a + P,block << 2);sort(b+P,b+block+P);}
//<<2因为copy的是字节
int a[N],b[N],t[2333];
int main(){
int n,block,op,l,r,c,i,ans;
scanf("%d",&n);
block = sqrt(n) / 2;
for(i = 0;i < n;++i) scanf("%d",&a[i]);
for(;i % block;++i) a[i] = 1e9;//减少vector常数
for(i = 0;i < n;i += block) resort(i);
for(i = 0;i < n;++i){
scanf("%d%d%d%d",&op,&l,&r,&c);
--l; -- r;
if(op){
ans = 0;c *= c;
for(;l <= r && l % block;++l) ans += a[l] + t[l/block] < c;
for(;l + block - 1 <= r;l += block)
ans += lower_bound(b+l,b+l+block,c-t[l/block])-b-l;
for(;l <= r;++l)ans += a[l]+t[l/block]<c;
printf("%d
",ans);
}
else{
if(l/block == r/block){//同块
for(;l <= r;++l)a[l] += c;
resort(r/block*block);
continue;
}
if(l %block){
for(;l % block;++l) a[l] += c;
resort(l - block);
}
for(;l + block-1 <= r;l += block) t[l / block] += c;
if(r >= l){
for(;r >= l;--r) a[r] += c;
resort(l);
}
}
}
}
入门3
区间加,区间内小于(x)的前驱(比其小的最大元素)
#define resort(P) {memcpy(b + P,a + P,block<<2);sort(b + P,b + P + block);}
using namespace std;
int a[N],b[N],t[N];
int main(){
int n,block,i,op,l,r,c,ans;
scanf("%d",&n);
for(i = 0;i < n;++i) scanf("%d",&a[i]);
block = sqrt(n);
for(;i % block;++i) a[i] = 1e10;
for(i = 0;i < n;i += block) resort(i);
for(i = 0;i < n;++i){
scanf("%d%d%d%d",&op,&l,&r,&c);
--l; --r;
if(op){
ans = -1;
for(;l <= r && l % block;++l)
if((op = a[l] + t[l/block]) < c && ans < op)
ans = op;
for(;l + block - 1 <= r;l += block){
if((op = lower_bound(b+l,b+l+block,c - t[l/block])-b-1) >= l &&
ans < b[op] + t[op/block])
ans = b[op] + t[op/block];
}
for(;l <= r;++l)
if((op = a[l] + t[l/block]) < c && ans < op)
ans = op;
printf("%d
",ans);
}
else{
if (l / block == r / block) {
for (; l <= r; ++l) a[l] += c;
resort(r / block * block);
}
if (l % block) {
for (; l % block; ++l) a[l] += c;
resort(l - block);
}
for (; l + block - 1 <= r; l += block) t[l / block] += c;
if (r >= l) {
for (; r >= l; --r) a[r] += c;
resort(l);
}
}
}
}
入门4
区间加 区间查询
线段树,不解释
int n,laz[N<<2],lson[N<<2],rson[N<<2];
long long tre[N<<2];
#define l(x) lson[x]
#define r(x) rson[x]
#define add(x) laz[x]
#define sum(x) tre[x]
void build(int x,int l,int r){
l(x) = l;r(x) = r;
if(l == r) {sum(x) = read();return;}
int mid = l + r >> 1;
build(x<<1,l,mid); build(x<<1|1,mid+1,r);
sum(x) = sum(x<<1) + sum(x<<1|1);
}
inline void updata(int x){
if(add(x)){
sum(x<<1) += add(x) * (r(x<<1) - l(x<<1) + 1);
sum(x<<1|1) += add(x) * (r(x<<1|1) - l(x<<1|1) + 1);
add(x<<1) += add(x); add(x<<1|1) += add(x);
add(x) = 0;
}
}
void change(int x,int l,int r,int a){
if(l <= l(x) && r >= r(x)){
tre[x] += a * (r(x) - l(x) + 1);
add(x) += a; return;
}
updata(x);
int mid = (l(x) + r(x)) >> 1;
if(l <= mid) change(x<<1,l,r,a);
if(r > mid) change(x<<1|1,l,r,a);
sum(x) = sum(x<<1) + sum(x<<1|1);
}
long long query(int x,int l,int r){
if(l <= l(x) && r >= r(x)) return sum(x);
updata(x);
int mid = (l(x) + r(x)) >> 1;
long long val = 0;
if(l <= mid) val += query(x<<1,l,r);
if(r > mid) val += query(x<<1|1,l,r);
return val;
}
int main(){
scanf("%d",&n);int op,l,r,c; build(1,1,n);
for(int i = 1;i <= n;++i){
scanf("%d%d%d%d",&op,&l,&r,&c);
if(op) printf("%lld
",query(1,l,r) % (c + 1));
else change(1,l,r,c);
}
}
入门5
区间开方 区间求和
花神游历各国
线段树
弄个标记表示当前节点区间已全部变为0或1,假如两个子区间都打标记了,那么当前区间也打上,以后区间开方碰到有标记就结束掉
#include<cstdio>
#include<cmath>
#define R register
#define G c=getchar()
inline void in(R int&z){
R char G;
while(c<'-')G;
z=c&15;G;
while(c>'-')z*=10,z+=c&15,G;
}
const int M=3000000;
int le[M],mi[M],ri[M],s[M],t[M];
#define lc u<<1
#define rc u<<1|1
#define pushup s[u]=s[lc]+s[rc],t[u]=t[lc]&t[rc]//上传和以及标记
void build(R int u,R int l,R int r){//建树
le[u]=l;ri[u]=r;mi[u]=(l+r)>>1;
if(l==r){
in(s[u]);
if(s[u]<2)t[u]=1;
return;
}
build(lc,l,mi[u]);
build(rc,mi[u]+1,r);
pushup;
}
void update(R int u,R int l,R int r){//更新,略超出模板范围
if(t[u])return;
if(le[u]==ri[u]){
s[u]=sqrt(s[u]);
if(s[u]<2)t[u]=1;
return;
}
if(r<=mi[u])update(lc,l,r);
else if(l>mi[u])update(rc,l,r);
else update(lc,l,mi[u]),update(rc,mi[u]+1,r);
pushup;
}
int ask(R int u,R int l,R int r){//查询完全是模板
if(l==le[u]&&r==ri[u])return s[u];
if(r<=mi[u])return ask(lc,l,r);
else if(l>mi[u])return ask(rc,l,r);
else return ask(lc,l,mi[u])+ask(rc,mi[u]+1,r);
}
int main(){
R int n,op,l,r,c;
in(n);
build(1,1,n);
while(n--){
in(op);in(l);in(r);in(c);
if(op)printf("%d
",ask(1,l,r));
else update(1,l,r);
}
return 0;
}
树状数组 + 并查集
对于第 i 个数 a[ i ],当 a[ i ] 不等于 1 时,他的祖先是他自己,即 f[ i ] = i,当 a[ i ] = 1 时,f[ i ] = 下一个不等于 1 的数的位置 j (i < j <=n+1),这里要注意 f[ n + 1 ] = n + 1.
然后我们在 l ~ r 区间内寻找祖先是自己的数修改即可,具体可用指针不断更新,find 找祖先.
#include<cstdio>
#include<cmath>
#define int long long
#define maxn 100005
using namespace std;
int k,l,r,n,m,a[maxn],c[maxn],fa[maxn];
inline int swap(int &a,int &b){int tem;tem = a;a = b;b = tem;}
inline int read(){
int X=1,sum=0;char ch=getchar();
while(ch>'9'||ch<'0')X=(ch=='-'?-1:1),ch=getchar();
while(ch<='9'&&ch>='0')sum=(sum<<3)+(sum<<1)+ch-'0',ch=getchar();
return X*sum;
}
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int y){for(;x<=n;x += lowbit(x)) c[x] += y;}
inline int ask(int x){
int ans = 0;
for(;x;x -= lowbit(x))
ans += c[x];
return ans;
}
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
signed main(){
n = read();
for(int i=1;i<=n;i++){
a[i] = read();
add(i,a[i]);
fa[i] = i;
}
fa[n+1] = n+1;
m = read();
while(m--){
k = read(),l = read(),r = read();
if(l > r) swap(l,r);
if(k) printf("%lld
",ask(r) - ask(l-1));
else while(l <= r){
int t = (int) sqrt(a[l]);
add(l,t - a[l]);a[l] = t;
fa[l] = a[l] <= 1 ? l+1 : l;
l = fa[l] == l ? l+1 : find(fa[l]);//如果这个数没有开方到1,到下一个数,否则找下一个为1的数
}
}
}
入门6
单点加 单点查
int main(){
scanf("%d",&n);
q.reserve(N); int opt,l,r,c;
for(int i = 1;i <= n;++i) q.push_back(read());
while(n--){
opt = read(); l = read(); r = read(); c = read();
if(opt)
printf("%d
",q[r-1]);
else
q.insert(q.begin() + l - 1,r);
}
}
入门7
区间加 乘 单点找
#define st tree
#define int long long
struct node{int v,add,mul;}tree[N<<2];
void build(int root,int l,int r){
tree[root].mul = 1;
tree[root].add = 0;
if(l == r) tree[root].v = a[l];//
else{
int mid = (l + r)>>1;
build(root<<1,l,mid); build(root<<1|1,mid +1,r);
tree[root].v = tree[root<<1].v + tree[root<<1|1].v;
}
tree[root].v %= p;
return;
}
void pushdown(int root,int l,int r){//维护lazy
int mid = (l + r)>>1;//儿子的值*爸爸的mul+儿子的区间长度*爸爸的add
tree[root<<1].v = (tree[root<<1].v * tree[root].mul + tree[root].add * (mid - l + 1))%p;
tree[root<<1|1].v = (tree[root<<1|1].v*tree[root].mul+tree[root].add*(r - mid))%p;
tree[root<<1].mul = (tree[root].mul * tree[root<<1].mul)%p;
tree[root<<1|1].mul = (tree[root].mul * tree[root<<1|1].mul)%p;
tree[root<<1].add = (tree[root].mul * tree[root<<1].add + tree[root].add)%p;
tree[root<<1|1].add = (tree[root].mul * tree[root<<1|1].add + tree[root].add)%p;
tree[root].mul = 1; tree[root].add = 0; return;
}
void tmul(int root,int stdl,int stdr,int l,int r,int k){
if(r < stdl || l > stdr) return;
if(l <= stdl && r >= stdr){
tree[root].v = (tree[root].v * k) % p;
tree[root].mul = (tree[root].mul * k) % p;
tree[root].add = (tree[root].add * k) % p;
return;
}
pushdown(root,stdl,stdr);
int mid = stdl + stdr >>1;
tmul(root<<1,stdl,mid,l,r,k); tmul(root<<1|1,mid+1,stdr,l,r,k);
tree[root].v = (tree[root<<1].v + tree[root<<1|1].v) % p;
return;
}
void tadd(int root,int stdl,int stdr,int l,int r,int k){
if(r < stdl || l > stdr) return;
if(l <= stdl && r >= stdr){
tree[root].add = (tree[root].add + k) % p;
tree[root].v = (tree[root].v + k * (stdr - stdl + 1)) % p;
return;
}
pushdown(root,stdl,stdr);
int mid = stdl + stdr >> 1;
tadd(root<<1,stdl,mid,l,r,k); tadd(root<<1|1,mid+1,stdr,l,r,k);
tree[root].v = (tree[root<<1|1].v + tree[root<<1].v) % p;
return;
}
int query(int root,int stdl,int stdr,int l,int r){
if(r < stdl || l > stdr) return 0;
if(l <= stdl && r >= stdr) return tree[root].v;
pushdown(root,stdl,stdr);
int mid = (stdl + stdr) >> 1;
return (query(root<<1,stdl,mid,l,r) + query(root<<1|1,mid+1,stdr,l,r)) % p;
}
signed main(){
int n;scanf("%lld",&n);p = 10007;
for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
build(1, 1, n);
for(int i = 1;i <= n;++i){
int qwq, x, y,k;
scanf("%lld",&qwq);scanf("%lld%lld%lld", &x, &y, &k);
if(qwq==1)
tmul(1, 1, n, x, y, k);
else if(qwq==0)
tadd(1, 1, n, x, y, k);
else
printf("%lld
", query(1, 1, n, y, y));
}
}