根号算法

根号算法

数论分块
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));
    }
}
入门8
原文地址:https://www.cnblogs.com/shikeyu/p/13762747.html