• 树状数组(单点/区间)操作模板


    相关讲解链接


    单点修改,区间查询(loj130/133)

    代码(一维)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    long long n,q,c[1000005],a[1000005];
    long long lowbit(long long x){return x&(-x);}
    void add(long long x,long long d){for(long long i=x;i<=n;i+=lowbit(i)) c[i]+=d;}
    long long ask(long long x)
    {
    	long long ans=0;
    	for(long long i=x;i>0;i-=lowbit(i)) ans+=c[i];
    	return ans;
    }
    int main()
    {
    	scanf("%lld%lld",&n,&q);
    	for(long long i=1;i<=n;++i){scanf("%lld",&a[i]); a[i]+=a[i-1];}
    	for(long long i=1;i<=q;++i)
    	{
    		long long f1,f2,f3; scanf("%lld%lld%lld",&f1,&f2,&f3);
    		if(f1==1) add(f2,f3);
    		else printf("%lld
    ",ask(f3)-ask(f2-1)+a[f3]-a[f2-1]);
    	}
    	return 0;
    }
    

    代码(二维)

    #include <iostream>
    #include <cstdio>
    #define maxn 4100
    using namespace std;
    int c[maxn][maxn],n,m;
    int lowbit(int x){return x&(-x);}
    void add(int x,int y,int d)
    {
    	for(int i=x;i<=n;i+=lowbit(i))
    		for(int j=y;j<=m;j+=lowbit(j)) c[i][j]+=d;
    }
    long long ask(int x,int y)
    {
    	long long ans=0;
    	for(int i=x;i;i-=lowbit(i))
    		for(int j=y;j;j-=lowbit(j)) ans+=c[i][j];
    	return ans;
    }
    int main()
    {
    	scanf("%d%d",&n,&m); int f1,f2,f3,f4,f5;
    	while(~scanf("%d%d%d%d",&f1,&f2,&f3,&f4))
    	{
    		if(f1==1) add(f2,f3,f4);
    		else scanf("%d",&f5),printf("%lld
    ",ask(f4,f5)-ask(f4,f3-1)-ask(f2-1,f5)+ask(f2-1,f3-1));
    	}
    	return 0;
    }
    

    区间修改,单点查询(loj131)

    代码(一维)+(不用差分的 偷懒正解)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    long long n,q,a[1000005],c[1000005];
    long long lowbit(long long x){return x&(-x);}
    void add(long long x,long long d){for(long long i=x;i<=1000000;i+=lowbit(i)) c[i]+=d;}
    long long ask(long long x)
    {
    	long long ans=0;
    	for(long long i=x;i>0;i-=lowbit(i)) ans+=c[i];
    	return ans;
    }
    int main()
    {
    	scanf("%lld%lld",&n,&q);
    	for(long long i=1;i<=n;++i) scanf("%lld",&a[i]);
    	for(long long i=1;i<=q;++i)
    	{
    		long long f1,f2,f3,f4; scanf("%lld",&f1);
    		if(f1==1)
    		{
    			scanf("%lld%lld%lld",&f2,&f3,&f4);
    			add(f2,f4); add(f3+1,-f4);
    		}
    		else
    		{
    			scanf("%lld",&f2);
    			printf("%lld
    ",ask(f2)+a[f2]);
    		}
    	}
    	return 0;
    }
    

    代码(一维)+(差分 教科书式解法)+(来源:标题下的链接)

    • 查询

    设原数组为a[i], 设数组d[i]=a[i]−ai−1,则a[i]=∑ij=1d[j],可以通过求d[i]的前缀和查询。

    • 修改

    当给区间[l,r]加上x的时候,a[l] 与前一个元素 a[l−1] 的差增加了x,a[r+1] 与 a[r] 的差减少了x。根据d[i]数组的定义,只需给d[l] 加上 x, 给d[r+1] 减去 x 即可。

    • 代码片段
    void add(int p, int x){ //这个函数用来在树状数组中直接修改
        while(p <= n) sum[p] += x, p += p & -p;
    }
    void range_add(int l, int r, int x){ //给区间[l, r]加上x
        add(l, x), add(r + 1, -x);
    }
    int ask(int p){ //单点查询
        int res = 0;
        while(p) res += sum[p], p -= p & -p;
        return res;
    }
    

    区间修改,区间查询(POJ3468/loj135)

    代码(一维)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    long long n,q,a[100005],sum1[100005],sum2[100005];
    long long lowbit(long long x){return x&(-x);}
    void add(long long x,long long d)
    {
    	for(long long i=x;i<=n;i+=lowbit(i))
    	{
    		sum1[i]+=d;
    		sum2[i]+=d*(x-1);
    	}
    }
    long long ask(int x)
    {
    	long long ans=0;
    	for(long long i=x;i>0;i-=lowbit(i)) ans+=x*sum1[i]-sum2[i];
    	return ans;
    }
    int main()
    {
    	scanf("%lld%lld",&n,&q);
    	for(int i=1;i<=n;++i)
    	{
    		scanf("%lld",&a[i]);
    		add(i,a[i]-a[i-1]);
    	}
    	while(q--)
    	{
    		char f; long long f1,f2,f3; cin>>f;
    		if(f=='Q')
    		{
    			scanf("%lld%lld",&f1,&f2);
    			printf("%lld
    ",ask(f2)-ask(f1-1));
    		}
    		else if(f=='C')
    		{
    			scanf("%lld%lld%lld",&f1,&f2,&f3);
    			add(f1,f3); add(f2+1,-f3);
    		}
    	}
    	return 0;
    }
    

    代码(二维)

    维护:d[i][j],d[i][j]∗i,d[i][j]∗j,d[i][j]∗i∗j (d[i][j]为a[i][j]差分数组)

    #include <iostream>
    #include <cstdio>
    #define int long long
    #define maxn 2050
    using namespace std;
    int n,m,sum1[maxn][maxn],sum2[maxn][maxn],sum3[maxn][maxn],sum4[maxn][maxn];
    int lowbit(int x){return x&(-x);}
    void add(int x,int y,int d)
    {
    	for(int i=x;i<=n;i+=lowbit(i))
    		for(int j=y;j<=m;j+=lowbit(j))
    		{
    			sum1[i][j]+=d;
    			sum2[i][j]+=x*d;
    			sum3[i][j]+=y*d;
    			sum4[i][j]+=x*y*d;
    		}
    }
    int ask(int x,int y)
    {
    	int ans=0;
    	for(int i=x;i;i-=lowbit(i))
    		for(int j=y;j;j-=lowbit(j))
    			ans+=(x+1)*(y+1)*sum1[i][j]-(y+1)*sum2[i][j]-(x+1)*sum3[i][j]+sum4[i][j];
    	return ans;
    }
    void addedge(int xa,int ya,int xb,int yb,int d){
    	add(xa,ya,d),add(xa,yb+1,-d),add(xb+1,ya,-d),add(xb+1,yb+1,d);
    }
    int askedge(int xa,int ya,int xb,int yb){
        return ask(xb,yb)-ask(xb,ya-1)-ask(xa-1,yb)+ask(xa-1,ya-1);
    }
    signed main()
    {
    	scanf("%lld%lld",&n,&m);
    	int op,f1,f2,f3,f4,f5;
    	while(~scanf("%lld%lld%lld%lld%lld",&op,&f1,&f2,&f3,&f4))
    	{
    		if(op==1) scanf("%lld",&f5),addedge(f1,f2,f3,f4,f5);
    		else printf("%lld
    ",askedge(f1,f2,f3,f4));
    	}
    	return 0;
    }
    
  • 相关阅读:
    poj 4005 Moles
    牛客 2C 圈圈
    牛客 2B 树 (组合计数)
    AC日记——校门外的树(增强版) 洛谷 P1276
    AC日记——寻找道路 洛谷 P2296
    AC日记——挤牛奶 洛谷 P1204
    AC日记——最大数 洛谷 P1198 [JSOI2008]
    AC日记——中位数 洛谷 P1168
    AC日记——校门外的树 洛谷 P1047
    AC日记——约瑟夫问题 codevs 1282
  • 原文地址:https://www.cnblogs.com/wuwendongxi/p/13434487.html
Copyright © 2020-2023  润新知