• 区间修改+区间查询


    以牛客网的小白赛5,第i题,为列!

    典型的区间修改+区间查询

    题目如下:

    链接:https://www.nowcoder.com/acm/contest/135/I
    来源:牛客网
     

    题目描述

        Apojacsleam喜欢数组。

        他现在有一个n个元素的数组a,而他要对a[L]-a[R]进行M次操作:

            操作一:将a[L]-a[R]内的元素都加上P

            操作二:将a[L]-a[R]内的元素都减去P

        最后询问a[l]-a[r]内的元素之和?

        请认真看题干及输入描述。

    输入描述:

    输入共M+3行:
    
    第一行两个数,n,M,意义如“题目描述”
    
    第二行n个数,描述数组。
    
    第3-M+2行,共M行,每行四个数,q,L,R,P,若q为1则表示执行操作2,否则为执行操作1
    
    第4行,两个正整数l,r

    输出描述:

    一个正整数,为a[l]-a[r]内的元素之和

    示例1

    输入

    复制

    10 5
    1 2 3 4 5 6 7 8 9 10
    1 1 5 5
    1 2 3 6
    0 2 5 5 
    0 2 5 8
    1 4 9 6
    2 7

    输出

    复制

    23

    说明

     

    这题目有很多解决的办法,但是要求时间复杂度符合要求。

    方法一:

    首先,很多acmer想到的肯定是树状数组,这确实很好用,时间复杂度,O(n*log(n))级别,读者可自行了解树状数组的原理及其模板。很好用

    代码如下:

    #include <cstdio>
    #define lowbit(x) (x&-x)
    #define ll long long
    const ll maxn=1000010;
    using namespace std;
    ll n, m, q, c1[maxn], c2[maxn], num[maxn];
    
    void add(ll *r, ll pos, ll v) {  //区间加
    	for(; pos<=n; pos+=lowbit(pos))r[pos]+=v;
    }
    
    void sub(ll *r,ll pos, ll v) {   //区间减
    	for(; pos<=n; pos+=lowbit(pos))r[pos]-=v;
    }
    
    ll sigma(ll *r, ll pos) {  //求区间和
    	ll ans;
    	for(ans=0; pos; pos-=lowbit(pos))ans+=r[pos];
    	return ans;
    }
    
    int main() {
    	ll i, j, type, a, b, v, sum1, sum2, q;
    	while(scanf("%d%d",&n,&m)==2) {
    		for(i=1; i<=n; i++) {  //输入原始数据
    			scanf("%lld",num+i);
    			add(c1,i,num[i]-num[i-1]);
    			add(c2,i,(i-1)*(num[i]-num[i-1]));
    		}
    		for(int i=0; i<m; i++) {
    			scanf("%lld%lld%lld%lld",&q,&a,&b,&v);
    			if(q!=1) {  //修改a,b之间,加上v 
    				add(c1,a,v);
    				add(c1,b+1,-v);
    				add(c2,a,v*(a-1));
    				add(c2,b+1,-v*b);
    			} else {    //修改a,b之间,减去v 
    				sub(c1,a,v);
    				sub(c1,b+1,-v);
    				sub(c2,a,v*(a-1));
    				sub(c2,b+1,-v*b);
    			}
    		}
    		scanf("%lld%lld",&a,&b);  //输入要求的a,b区间 
    		sum1=(a-1)*sigma(c1,a-1)-sigma(c2,a-1); 
    		sum2=b*sigma(c1,b)-sigma(c2,b);
    		printf("%lld
    ",sum2-sum1);  //a,b前缀和相减 
    	}
    	return 0;
    }
    

    方法二:

    先把修改的命令都先储存起来,然后再与之最后要求的区间相匹配。

    首先,add=还没开始修改的区间和;然后做一下处理

    如果修改的区间与要求的区间相重合,则:add+=(重合的长度)*(p)  或   add-=(重合的长度)*(p)

    时间复杂度为O(N),很巧妙的方法,当然只限于此题,不通用。

    详细ac代码如下:

    #include<iostream>
    #include<cstring>
    using namespace std;
     
    const int maxn=1000000+5;
    int num[maxn];
    int q[maxn],l[maxn],r[maxn],p[maxn];
     
    int main() {
        int n,m,a,b;
        while(scanf("%d%d",&n,&m)==2) {
            for(int i=1; i<=n; i++) {
                scanf("%d",&num[i]);
            }
            for(int i=0; i<m; i++) {
                scanf("%d%d%d%d",&q[i],&l[i],&r[i],&p[i]);  //把这个所有输入储存起来 
            }
            unsigned long long add=0;
            scanf("%d%d",&a,&b);  //根据最后确定的区间,只处理这个区间里面的 
            for(int i=a; i<=b; i++)  //先求出a,b区间的和 
                add+=num[i];
            for(int i=0; i<m; i++) {  
                if(q[i]==1) {
                    if(l[i]<=r[i])  //注意这很关键,有可能数据l>r,不合法 
                        if(l[i]<a) {   //四种区间重合的情况 
                            if(r[i]>=a&&r[i]<=b) {
                                add-=(r[i]-a+1)*p[i];
                            } else if(r[i]>b) {
                                add-=(b-a+1)*p[i];
                            }
                        } else if(a<=l[i]&&l[i]<=b) {
                            if(a<=r[i]&&r[i]<=b) {
                                add-=(r[i]-l[i]+1)*p[i];
                            } else if(r[i]>b) {
                                add-=(b-l[i]+1)*p[i];
                            }
                        }
                } else {
                    if(l[i]<=r[i])
                        if(l[i]<a) {
                            if(r[i]>=a&&r[i]<=b) {
                                add+=(r[i]-a+1)*p[i];
                            } else if(r[i]>b) {
                                add+=(b-a+1)*p[i];
                            }
                        } else if(a<=l[i]&&l[i]<=b) {
                            if(a<=r[i]&&r[i]<=b) {
                                add+=(r[i]-l[i]+1)*p[i];
                            } else if(r[i]>b) {
                                add+=(b-l[i]+1)*p[i];
                            }
                        }
                }
            }
            printf("%lld
    ",add);
        }
        return 0;
    }
  • 相关阅读:
    Redis作者谈Redis应用场景(转)
    程序员必读书籍及导读指南(转)
    Python迭代器包itertools(转)
    Flash 0day漏洞(CVE-2018-4878)复现
    第二届“强网杯”全国网络安全挑战赛来袭——线上赛
    网站漏洞——文件判断函数的安全风险(实战篇)
    Android逆向进阶——让你自由自在脱壳的热身运动(dex篇)
    【python入门】之教你编写自动获取金币脚本
    Python大法之从火车余票查询到打造抢Supreme神器
    读DEDECMS找后台目录有感
  • 原文地址:https://www.cnblogs.com/horken/p/10706159.html
Copyright © 2020-2023  润新知