zoukankan      html  css  js  c++  java
  • 根号算法

    根号算法

    数论分块
    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
  • 相关阅读:
    java不定参数列表---乔老师没讲,但是传智有讲
    java数据库连接模板代码通用收集
    java数据库连接模板代码通用收集
    BZOJ2060: [Usaco2010 Nov]Visiting Cows 拜访奶牛
    BZOJ1598: [Usaco2008 Mar]牛跑步
    BZOJ1710: [Usaco2007 Open]Cheappal 廉价回文
    manacher模板
    BZOJ1584: [Usaco2009 Mar]Cleaning Up 打扫卫生
    BZOJ1753: [Usaco2005 qua]Who's in the Middle
    BZOJ1828: [Usaco2010 Mar]balloc 农场分配
  • 原文地址:https://www.cnblogs.com/shikeyu/p/13762747.html
Copyright © 2011-2022 走看看