zoukankan      html  css  js  c++  java
  • 多种方法求解Pku3468 A Simple Problem with Integers

    You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of op
    eration is to add some given number to each number in a given interval. The other is to ask for the
    sum of numbers in a given interval.
    1.给[a ,b]整体上加上一个常数c。
    2.查询[a ,b]区间的和。
    Input
    The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
    The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
    Each of the next Q lines represents an operation.
    "C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
    "Q a b" means querying the sum of Aa, Aa+1, ... , Ab.
    Output
    You need to answer all Q commands in order. One answer in a line. The sums may exceed the range of 32-bit integers
    Sample Input
    10 5
    1 2 3 4 5 6 7 8 9 10
    Q 4 4
    Q 1 10
    Q 2 4
    C 3 6 3
    Q 2 4
    Sample Output
    4
    55
    9
    15

    Sol1:Lazy标记

    #include<bits/stdc++.h>
    using namespace std;
    long long sum[800000],n,q,tt[800000],lazy[800000],size[800000],a1,a2,a3;
    char s;
    void a_lazy(int p,int v) //打Lazy标记操作
    {
    	sum[p]+=size[p]*v;  //更新SUM的值,体现加值的操作效果
    	lazy[p]+=v;             
    }
    void push_down(int p)  //下放标记,其与上面那个是两个含义
    {
    	a_lazy(p*2,lazy[p]);
                    a_lazy(p*2+1,lazy[p]);
    	lazy[p]=0;
    }
    void updata(int p)
    {
    	sum[p]=sum[p*2]+sum[p*2+1]; 
                   //用左右子结点的SUM值“更新”父亲点的,注意这里是赋值,而不是累加
                   //好比我们对[1..10]加了5,于是标记只打在[1..10]上,并不下传
                  //然后对[1..5]加上6,于是加5的标记要传到两个子结点上,然后再将加6的标记传到[1..5】上
                  //于是得到[1..5]的权值为5*11=55,[6..10]的权值为5*5=25
                  //于是[1..10]的权值为55+25=80
    	size[p]=size[p*2]+size[p*2+1]; 
    }
    void build(int p,int l,int r)
    {
    	if(l==r)
    	{
    		sum[p]=tt[l],size[p]=1;
    		return;
    	}
    	int mid=(l+r)/2; 
    	build(p*2,l,mid);build(p*2+1,mid+1,r);
    	updata(p);
    }
    void q_add(int p,int l,int r,int x,int y,int v)
    {
    	if(l>=x&&r<=y)  //只有当线段树上区间为当前操作区间的子集时,才打上lazy标记
    	{
    		a_lazy(p,v);
    		return; 
    	}
                    //如果没有返回的话,说明线段树上的区间大于当前操作的区间
    	int mid=(l+r)/2;
    	push_down(p);  //此时要将P上的标记,进行下传
    	if(mid>=x)q_add(p*2,l,mid,x,y,v);
    	if(mid<y)q_add(p*2+1,mid+1,r,x,y,v);
    	updata(p);  //信息及时上传
    }
    long long query(int p,int l,int r,int x,int y)
    {
    	if(l>=x&&r<=y) //sum[p]是真实体现这一段数字和的,标记带来的影响已统计进去了
                           return sum[p];
    	long long ans=0;
    	push_down(p);  
                     //此时也要进行标记下传,因为要统计的区间可能从前没有访问过,所以标记根本没有下传, 其sum值也不是真实的
    	int mid=(l+r)/2; 
    	if(mid>=x)ans+=query(p*2,l,mid,x,y);
    	if(mid<y)ans+=query(p*2+1,mid+1,r,x,y);
    	return ans;
    }
    int main()
    {
    	scanf("%lld%lld",&n,&q);
    	for(int i=1;i<=n;i++)scanf("%lld",&tt[i]);
    	build(1,1,n);
    	for(int i=1;i<=q;i++)
    	{
    		cin>>s;
    		if(s=='Q')
    		{
    			scanf("%lld%lld",&a1,&a2);
    			printf("%lld
    ",query(1,1,n,a1,a2));
    		}
    		else
    		{
    			scanf("%lld%lld%lld",&a1,&a2,&a3);
    			q_add(1,1,n,a1,a2,a3);
    		}
    	}
    	
        return 0;
    }
    

      

    #include<cstdio>
    #define mid (s[x].l+s[x].r>>1)
    int n,m,d[100001];
    char p[3];
    struct oo{int l,r;long long lazy,sum;}s[400001];
    void build(int x,int l,int r)
    {
        s[x].l=l,s[x].r=r;
        if(l==r){s[x].sum=d[l];return ;}
        build(x<<1,l,mid);build(x<<1|1,mid+1,r);
        s[x].sum=s[x<<1].sum+s[x<<1|1].sum;
    }
    void pushdown(int x)  //lazy标记下传
    {
        int l=x<<1,r=x<<1|1;
        s[l].sum+=(s[l].r-s[l].l+1)*s[x].lazy;
        s[r].sum+=(s[r].r-s[r].l+1)*s[x].lazy;
        s[l].lazy+=s[x].lazy;s[r].lazy+=s[x].lazy;
        s[x].lazy=0;
    }
    void change(int x,int l,int r,int v)
    {
        if(l<=s[x].l&&r>=s[x].r)
        {
            s[x].sum+=(s[x].r-s[x].l+1)*v;
            s[x].lazy+=v;
            return ;
        }
        if(s[x].lazy)  //记得标记下传
            pushdown(x);
        if(l<=mid)
            change(x<<1,l,r,v);
        if(r>mid)
            change(x<<1|1,l,r,v);
        s[x].sum=s[x<<1].sum+s[x<<1|1].sum;
    }
    long long get(int x,int l,int r)
    {
        if(l<=s[x].l&&r>=s[x].r)
            return s[x].sum;
        if(s[x].lazy)       //记得标记下传
           pushdown(x);
        long long ans=0;
        if(l<=mid)ans+=get(x<<1,l,r);
        if(r>mid)ans+=get(x<<1|1,l,r);
        s[x].sum=s[x<<1].sum+s[x<<1|1].sum;
        return ans;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%d",&d[i]);
        build(1,1,n);int x,y,z;
        while(m--)
        {
            scanf("%s",p+1);
            if(p[1]=='Q')
            {
                scanf("%d%d",&x,&y);
                printf("%lld
    ",get(1,x,y));
            }
            else
            {
                scanf("%d%d%d",&x,&y,&z);
                change(1,x,y,z);
            }
        }
    }
    

      

    所谓标记永久化,即只在线段树上的区间为当前操作区间的子集时,
    才打上标记,且标记并不下发到子结点。

    //设树的结构为 p1
    //                      /
    //                   p2
    //                   /
    //               p3
    //当我们有个标记打在p2上面时,则从p1开始走,就事先算出对p1的影响
    //由于影响已算出来了,即sum[p1]体现的就是这一段真实的数字和
    //于是也就用不着updata上传信息了
    //然后走到p2,算出对p2的影响,由于并没有接着向下走
    //所以事实上是对p3区间是有影响的,但标记没有下传
    //于是在算p3的时候,需要累加从p1到p2这一段的标记量的累加

    下面这个代码是Pku3468 A Simple Problem with Integers,代码来自zy

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+5;
    int n,m;
    ll a[maxn];
    ll sum[maxn<<2],add[maxn<<2];
    void updata(int p)
    {
    sum[p]=sum[p<<1]+sum[p<<1|1];
    }
    void build(int p,int l,int r)
    {
    if(l==r)
    {
             sum[p]=a[l];
             return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    updata(p);
    }
    void change(int p,int l,int r,int L,int R,ll v)
    {
    sum[p]+=v*(min(R,r)-max(L,l)+1); 
    //记录下对区间P的实际贡献 
    if(L<=l&&r<=R)
    {
            add[p]+=v;
            return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) //如果操作的左边界小于等于mid时,落在左子树 
    change(p<<1,l,mid,L,R,v);
    
    if(R>mid) //如果操作的右边界大于mid,落在右子树 
    change(p<<1|1,mid+1,r,L,R,v);
    
    }
    ll query(int p,int l,int r,int L,int R)
    {
    if(L<=l&&r<=R)
        return sum[p];
    int mid=(l+r)>>1;
    ll res=add[p]*(min(R,r)-max(L,l)+1);
    //累加一路走来,遇到的标记,注意res是个局部变量
    if(L<=mid)
        res+=query(p<<1,l,mid,L,R);
    if(R>mid)
        res+=query(p<<1|1,mid+1,r,L,R);
    
    return res;
    }
    int main() 
    {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%lld",a+i);
    build(1,1,n);
    for(int i=1;i<=m;i++) {
    int opt,l,r;ll v;
    char p[3];
    scanf("%s",p+1);
    int x,y;scanf("%d%d",&l,&r);
    if(p[1]=='C')
    {
    scanf("%lld",&v);
    change(1,1,n,l,r,v);
    }
    else printf("%lld
    ",query(1,1,n,l,r));
    }
    return 0;
    }
    

      这个是xyh的

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
     
    const int maxn=1e5+5;
     
    int n,m;
    ll a[maxn];
     
    struct segment_tree 
    {
        ll sum[maxn<<2],add[maxn<<2];
        void updata(int p) 
    	{
            sum[p]=sum[p<<1]+sum[p<<1|1];
        }
        void build(int p,int l,int r) 
    	{
            if(l==r) {sum[p]=a[l];return;}
            int mid=(l+r)>>1;
            build(p<<1,l,mid);
            build(p<<1|1,mid+1,r);
            updata(p);
        }
        void change(int p,int l,int r,int L,int R,ll v) 
    	{
            sum[p]+=v*(R-L+1);
            if(L<=l&&r<=R) 
    		{
    			 add[p]+=v;
    			 return;
    		}
            int mid=(l+r)>>1;
            if(R<=mid)
    		   change(p<<1,l,mid,L,R,v);
            else 
    		    if(L>mid)
    			   change(p<<1|1,mid+1,r,L,R,v);
                else 
    			  change(p<<1,l,mid,L,mid,v),change(p<<1|1,mid+1,r,mid+1,R,v);
        }
     
        ll query(int p,int l,int r,int L,int R) 
    	{
            if(L<=l&&r<=R)
    		   return sum[p];
            int mid=(l+r)>>1;
    		ll res=add[p]*(R-L+1);  //累加一路走来,遇到的标记
            if(R<=mid)
    		   res+=query(p<<1,l,mid,L,R);
            else 
    		   if(L>mid)
    		      res+=query(p<<1|1,mid+1,r,L,R);
               else 
    		    res+=query(p<<1,l,mid,L,mid)+query(p<<1|1,mid+1,r,mid+1,R);
            return res;
        }
    }T;
     
    int main() {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%lld",a+i);
        T.build(1,1,n);
        for(int i=1;i<=m;i++) {
            int opt,l,r;ll v;
            char p[3];
          
            scanf("%s",p+1);
            int x,y;scanf("%d%d",&l,&r);
            if(p[1]=='C')
    		{
                scanf("%lld",&v);
                T.change(1,1,n,l,r,v);
            }
            else printf("%lld
    ",T.query(1,1,n,l,r));
        }
        return 0;
    }
    

      

     
     

      下面这个是ljq的

    //s[p]代表p这一段的数字和,但凡有标记打在p及p的子结点上,都会体现在s[p]上
    //但是在p之上结点打过的标记,则不会体现出来。于是要累加一路走下来的标记
    //设树的结构为    p1
    //                          /
    //                       p2
    //                       /
    //                    p3
    //                     /
    //                 p4
    //设目标有标记打在p2上,则这个影响量开始只算出在p2上面的,
    //即s[p2]的值是真实这一段的数字之和
    //然后利用updata上传到p1上
    //同理,如果有标记打在p3上,也是开始只算出p3的,然后上传到p2,p1上
    //由于修改p2,p3,对p4也是有影响的,但标记并没有下传
    //于是在算p4的时候,要累加从p1到p3这一段的标记量之和

    //标记不下传,只当线段树上区间是目前操作区间的子集时,才打上标记
    //这样对于父区间来说,如果标记只打在子区间上,sum的集合是不完整的,于是要updata
    //在统计信息的时候,sum代表的时,所有标记打在这个区间上的
    //于是因为标记没下传,于是统计要要加上一路走过来时所要累加的量
    //例如对[1..10]打上10,[1..5]打上8
    //则统计[1..10]的总和时,直接返回sum[1](加上8的那部分在updata时传上来了) 
    //统计[1..5]时,要返回sum[2]+一路走过的标记*5 
    #include<cstdio>
    #include<iostream>
    typedef long long ll;
    using namespace std;
    ll a[100001];
    struct node
    {
        #define ls (p<<1)
        #define rs (p<<1|1)
        ll s[400001],cnt[400001];
        void updata(int p,int l,int r)
        {
            s[p]=s[ls]+s[rs]+cnt[p]*(r-l+1);
        }
        void build(int p,int l,int r)
        {
            cnt[p]=0;
            if(l==r)
            {
                s[p]=a[l];return ;
            }
            int mid=(l+r)>>1;
            build(ls,l,mid);
            build(rs,mid+1,r);
            updata(p,l,r);
        }
        void change(int p,int l,int r,int x,int y,int v)
        //l,r是线段树上的区间
    	//x,y是当前要操作的 
        {
            if(x<=l&&r<=y) //只有当目前线段树上的区间是要操作的区间的子集时 
            {
                	cnt[p]+=v; //加上标记 
                	s[p]+=1LL*(r-l+1)*v;
                 	return ;
            }
            int mid=(l+r)>>1;
            if(x<=mid)change(ls,l,mid,x,y,v);
            if(y>mid)change(rs,mid+1,r,x,y,v);
            updata(p,l,r);
        }
        long long ans;
        void query(int p,int l,int r,int x,int y,int cc)
        {
        //    printf("%d %d %d %d %d %lld %lld
    ",p,l,r,x,y,s[p],cnt[p]);
            if(x<=l&&r<=y)
    		{
    	           ans+=s[p];
       	           ans+=1LL*cc*(r-l+1);
    	           return;
            }
            int mid=(l+r)>>1;
            if(x<=mid)query(ls,l,mid,x,y,cc+cnt[p]);
            if(y>mid)query(rs,mid+1,r,x,y,cc+cnt[p]);  
        }
    }tree;
    int main()
    {
        int n,q,x,y,z;
        char c;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
          scanf("%lld",&a[i]);
        tree.build(1,1,n);
        for(int i=1;i<=q;i++)
        {
            cin>>c;
            scanf("%d%d",&x,&y);
            if(c=='Q')
    		{
    	           tree.ans=0;
    	           tree.query(1,1,n,x,y,0);
    	           printf("%lld
    ",tree.ans);
            }
    		else
    		{
    			scanf("%d",&z);
    			tree.change(1,1,n,x,y,z);}
            }
    }
    

      

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define pos(i,a,b) for(int i=(a);i<=(b);i++)
    #define N 201000
    using namespace std;
    int n,m;char p[3];
    long long sum[N*4],add[N*4];
    int a[N];
    void build(int l,int r,int rt)
    {
        if(l==r)
    	{
            sum[rt]=a[l];return;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        sum[rt]=sum[rt<<1]+sum[rt<<1|1];
    }
    void update(int rt,int l,int r,int v,int xl,int xr)
    {
        sum[rt]+=v*(min(xr,r)-max(xl,l)+1);
        if(l>=xl&&r<=xr) //等到区间完全重合时,再给add加上v增加的标记 
    	{
            add[rt]+=v; 
    		return;
        }
        int mid=(l+r)>>1;
        if(xr<=mid)  
    	   update(rt<<1,l,mid,v,xl,xr);
        else
    	{
            if(xl>mid)   
    		   update(rt<<1|1,mid+1,r,v,xl,xr);
            else 
    		   update(rt<<1,l,mid,v,xl,xr),update(rt<<1|1,mid+1,r,v,xl,xr);
    		  
        }
    }
    long long query(int rt,int ad,int l,int r,int xl,int xr)
    {
        if(xl<=l&&xr>=r)
    	{
            return sum[rt]+ad*(min(xr,r)-max(xl,l)+1);
        }  
        int mid=(l+r)>>1;
        if(xr<=mid) 
    	   return query(rt<<1,ad+add[rt],l,mid,xl,xr);
        else
    	{
         if(xl>mid) 
    	  return query(rt<<1|1,ad+add[rt],mid+1,r,xl,xr);
         else 
    		return query(rt<<1,ad+add[rt],l,mid,xl,xr)+query(rt<<1|1,ad+add[rt],mid+1,r,xl,xr);
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        pos(i,1,n) scanf("%d",&a[i]);
        build(1,n,1);
        pos(i,1,m)
    	{
            scanf("%s",p+1);
            int x,y;scanf("%d%d",&x,&y);
            if(p[1]=='C')
    		{
                int k;scanf("%d",&k);
                update(1,1,n,k,x,y);
            }
            else printf("%lld
    ",query(1,0,1,n,x,y));
        }
        return 0;
    }
    

    使用动态开点解决上述问题

    题目描述
    如题,已知一个数列,你需要进行下面两种操作:
    1.将某区间每一个数加上x
    2.求出某区间每一个数的和
    输入输出格式
    输入格式:
    第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
    第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
    接下来M行每行包含3或4个整数,表示一个操作,具体如下:
    操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k
    操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和
    输出格式:
    输出包含若干行整数,即为所有操作2的结果。
    输入输出样例
    输入样例#1:
    5 5
    1 5 4 2 3
    2 2 4
    1 2 3 2
    2 3 4
    1 1 5 1
    2 1 4
    输出样例#1:
    11
    8
    20
    说明
    时空限制:1000ms,128M
    数据规模:
    对于30%的数据:N<=8,M<=10
    对于70%的数据:N<=1000,M<=10000
    对于100%的数据:N<=100000,M<=100000
    (数据已经过加强_,保证在int64/long long数据范围内)

    #include<cstdio>
    #include<iostream>
    using namespace std;
    const int N=300010;
    struct node{
    	int ls,rs,lazy;
    	long long sum;
    }tr[N];
    int root=0,cnt=0;
    void insert(int &root,int l,int r,int ll,int rr,int x)
    {
    	if(!root) //动态开点 
    	    root=++cnt;
    	int b=min(r,rr)-max(l,ll)+1;
    	tr[root].sum+=b*x;
    	if(l>=ll&&r<=rr)
    	{
    		tr[root].lazy+=x;
    		return;
    	}
    	int mid=l+r>>1;
    	if(ll<=mid)
    	   insert(tr[root].ls,l,mid,ll,rr,x);
    	if(rr>mid)
    	   insert(tr[root].rs,mid+1,r,ll,rr,x);
    } 
    long long query(int root,int l,int r,int ll,int rr)
    {
    	if(l>=ll&&r<=rr)
    	   return tr[root].sum;
    	int mid=l+r>>1;
    	if(tr[root].lazy) //lazy标记下传 
    	{
    		if(!tr[root].ls)
    		   tr[root].ls=++cnt;
    		tr[tr[root].ls].lazy+=tr[root].lazy;
    		tr[tr[root].ls].sum+=tr[root].lazy*(mid-l+1);
    		if(!tr[root].rs)
    		   tr[root].rs=++cnt;
    		tr[tr[root].rs].lazy+=tr[root].lazy;
    		tr[tr[root].rs].sum+=tr[root].lazy*(r-mid);
    		tr[root].lazy=0;
    	}
    	long long ans=0;
    	if(ll<=mid)
    	   ans+=query(tr[root].ls,l,mid,ll,rr);
    	if(rr>mid)
    	   ans+=query(tr[root].rs,mid+1,r,ll,rr);
    	return ans;
    }
    int main()
    {
    	int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
    	{
            int temp;
            cin>>temp;
            insert(root,1,n,i,i,temp);
        }
        for(int i=1;i<=m;i++)
    	{
            int ta,tb,tc,td;
            cin>>ta>>tb>>tc;
            if(ta==1)
    		{
                cin>>td;
                insert(root,1,n,tb,tc,td);
            }
    		else 
    		if(ta==2)
    		{
                cout<<query(root,1,n,tb,tc)<<endl;
            }
        }
        return 0;
    }
    

      

  • 相关阅读:
    axis2 WebService的发布与调用
    sql语句having子句用法,很多时候你曾忘掉
    linux下tomcat开机自启动
    框架使用的技术主要是SpringMVC 在此基础上进行扩展
    SpringMVC整合Mongodb开发 架构搭建
    解决Linux下3T硬盘分区只有2T(2199G)可用
    ubuntu cp(copy) command
    Linux如何根据UUID自动挂载磁盘分区
    python exec和eval
    在OpenERP报表中使用selection 类型字段
  • 原文地址:https://www.cnblogs.com/cutemush/p/13289075.html
Copyright © 2011-2022 走看看