zoukankan      html  css  js  c++  java
  • 牛客3 F/G 牛牛的Link Power |线段树区间修改

    F题 牛牛的Link Power I

    https://ac.nowcoder.com/acm/contest/3004/F

    思路

    每个“1”对于它本身位置产生的影响贡献为0,而往后面依次产生了0,1,2,3,4,5...的贡献。

    参考下图,对于值为1的i号点,我们把线段树叶节点维护的[i+1,n]的值都+1(区间修改);每次查询值为1的点,query(1,i),查询出i点前所有为1的点对它的能量贡献,累加就可以了。

    建一颗线段树,区间修改,区间求和,累加查询i点前所有为1的点对i点的能量贡献即可。

    为什么区间+1之后,可以用线段树查询i点前所有为1的点对i点的能量贡献,
    因为 对于i点将[i+1,n]都加1,相当于把它影响到的点都权值+了1;
    不好理解的话,就参考下图的例子

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5+10;
    const ll mod = 1e9+7;
    int n,q,a[maxn];
    char s[maxn];
    ll sumv[maxn<<2],addv[maxn<<2];
    
    //合并 
    void pushup(int o){
        sumv[o] = sumv[o<<1] + sumv[o<<1|1];
    }
    
    //建树 
    void build(int o,int l,int r){
        if(l == r) { //到最后一行了 [1,1] [2,2] ... 
            sumv[o] = 0;
            return;
        }
        int mid = (l+r)>>1; 
        build(o<<1,l,mid);
        build(o<<1|1,mid+1,r);
        pushup(o);//向上合并 
    }
    
    void puttag(int o,int l,int r,ll v){
        addv[o] += v;
        sumv[o] += (r-l+1)*v;
    }
    
    void pushdown(int o,int l,int r){
        if(addv[o] == 0) return;
        addv[o<<1] += addv[o];
        addv[o<<1|1] += addv[o];
        int mid = (l+r)>>1;
        sumv[o<<1] += addv[o] * (mid-l+1);
        sumv[o<<1|1] += addv[o] * (r-mid);
        addv[o] = 0;
    } 
    
    void optadd(int o,int l,int r,int ql,int qr,ll v){
        if(ql<=l&&r<=qr){ //1.完全覆盖(l,r)这个子区间 就先更新好值,打上标记(为后面作标记准备)
            puttag(o,l,r,v); //在puttag这里更新区间的值 打上标记 
            return;
        }
        int mid = (l+r)>>1;
        pushdown(o,l,r);//标记下放 因为接下来要更新子区间了
        if(ql <= mid) optadd(o<<1,l,mid,ql,qr,v);
        if(qr > mid) optadd(o<<1|1,mid+1,r,ql,qr,v);
        pushup(o);
    }
    
    ll querysum(int o,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr) return sumv[o];
        ll ans = 0;
        int mid = (l+r)>>1;
        pushdown(o,l,r);
        if(ql<=mid) ans+=querysum(o<<1,l,mid,ql,qr);
        if(qr>mid) ans+=querysum(o<<1|1,mid+1,r,ql,qr);
        return ans;
    }
    
    
    int main(){
    	cin>>n;
    	scanf("%s",s+1);
    	build(1,1,n);
    	//query(i+1 ~ n) 求i点之前的所有点对它的贡献
    	ll ans = 0;
    	for(int i=1;i<=n;i++){
    		if(s[i] == '1'){
    			optadd(1,1,n,i+1,n,1); //i+1~n 区间+1 
    			ans = (ans + querysum(1,1,n,1,i))%mod; //求i点之前的所有点对它的贡献
    		}
    	}
    	cout<<ans;
    	return 0;
    }
    

    G题 牛牛的Link Power II

    https://ac.nowcoder.com/acm/contest/3004/G

    思路

    考虑这道题是F题的带修改操作,这样我们只建一颗线段树是不行的,因为我们要考虑“待修改的点”分别对它后面所有点和它后面所有点的影响
    pre线段树:查询求和:i点之前的所有点对它的贡献 (i对它前面所有点的影响)
    suf线段树:查询求和:i点之后的所有点对它的贡献 (i对它前面所有点的影响)

    这里多了一颗suf线段树,参考F题思路和下图,再建一个后缀suf

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5+10;
    const ll mod = 1e9+7;
    int n,m,q,a[maxn];
    char s[maxn];
    
    struct seg_tree{
    	
    	ll sumv[maxn<<2],addv[maxn<<2];
    	
    	//合并 
    	void pushup(int o){
    	    sumv[o] = sumv[o<<1] + sumv[o<<1|1];
    	}
    	
    	//建树 
    	void build(int o,int l,int r){
    	    if(l == r) { //到最后一行了 [1,1] [2,2] ... 
    	        sumv[o] = 0;
    	        return;
    	    }
    	    int mid = (l+r)>>1; 
    	    build(o<<1,l,mid);
    	    build(o<<1|1,mid+1,r);
    	    pushup(o);//向上合并 
    	}
    	
    	void puttag(int o,int l,int r,ll v){
    	    addv[o] += v;
    	    sumv[o] += (r-l+1)*v;
    	}
    	
    	void pushdown(int o,int l,int r){
    	    if(addv[o] == 0) return;
    	    addv[o<<1] += addv[o];
    	    addv[o<<1|1] += addv[o];
    	    int mid = (l+r)>>1;
    	    sumv[o<<1] += addv[o] * (mid-l+1);
    	    sumv[o<<1|1] += addv[o] * (r-mid);
    	    addv[o] = 0;
    	} 
    	
    	void optadd(int o,int l,int r,int ql,int qr,ll v){
    	    if(ql<=l&&r<=qr){ //1.完全覆盖(l,r)这个子区间 就先更新好值,打上标记(为后面作标记准备)
    	        puttag(o,l,r,v); //在puttag这里更新区间的值 打上标记 
    	        return;
    	    }
    	    int mid = (l+r)>>1;
    	    pushdown(o,l,r);//标记下放 因为接下来要更新子区间了
    	    if(ql <= mid) optadd(o<<1,l,mid,ql,qr,v);
    	    if(qr > mid) optadd(o<<1|1,mid+1,r,ql,qr,v);
    	    pushup(o);
    	}
    	
    	ll querysum(int o,int l,int r,int ql,int qr){
    	    if(ql<=l&&r<=qr) return sumv[o];
    	    ll ans = 0;
    	    int mid = (l+r)>>1;
    	    pushdown(o,l,r);
    	    if(ql<=mid) ans+=querysum(o<<1,l,mid,ql,qr);
    	    if(qr>mid) ans+=querysum(o<<1|1,mid+1,r,ql,qr);
    	    return ans;
    	}
    
    }pre,suf; 
    
    
    int main(){
    	cin>>n;
    	scanf("%s",s+1);
    	pre.build(1,1,n);
    	suf.build(1,1,n);
    	//pre.query(1 ~ x) 求i点之前的所有点对它的贡献 (i对它前面所有点的影响) 
    	//suf.query(x ~ n) 与上面相反 
    	ll ans = 0;
    	//先做一次计算求所有ans 并对pre和suf线段树更新加点 
    	for(int i=1;i<=n;i++){
    		if(s[i] == '1'){
    			ans = (ans + pre.querysum(1,1,n,1,i))%mod; //求i点之前的所有点对它的贡献
    			if(i!=n) pre.optadd(1,1,n,i+1,n,1); //i+1~n 区间+1 
    			if(i!=1) suf.optadd(1,1,n,1,i-1,1); 
    		}
    	}
    	cout<<ans<<endl;
    	cin>>m;
    	int q,x;
    	for(int i=1;i<=m;i++){
    		cin>>q>>x;
    		ll preSum = 0,sufSum = 0;
    		preSum = pre.querysum(1,1,n,1,x); //求i前所有点 对i的贡献pre 
    		sufSum = suf.querysum(1,1,n,x,n); //求i后所有点 对i的贡献pre 
    		if(q == 1){
    			ans = (ans + preSum + sufSum)%mod;
    			if(x!=n) pre.optadd(1,1,n,x+1,n,1);
    			if(x!=1) suf.optadd(1,1,n,1,x-1,1);
    		}else{
    			ans = ((ans - preSum - sufSum)%mod+mod)%mod;
    			if(x!=n) pre.optadd(1,1,n,x+1,n,-1);
    			if(x!=1) suf.optadd(1,1,n,1,x-1,-1);
    		}
    		cout<<ans%mod<<endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Java内存模型原理,你真的理解吗?
    CentOS 7.4 下搭建 Elasticsearch 6.3 搜索群集
    CentOS 7下ElasticSearch集群搭建案例
    分布式系统理论基础
    分布式系统理论进阶
    Paxos,Raft,Zab一致性协议-Raft篇
    P2P 网络核心技术:Gossip 协议
    分布式系统Paxos算法
    Hacker News的热门排名算法(转)
    Elasticsearch分布式机制和document分析
  • 原文地址:https://www.cnblogs.com/fisherss/p/12287606.html
Copyright © 2011-2022 走看看