A Simple Problem with Integers(POJ 3468)

  • 原题如下:
    A Simple Problem with Integers
    Time Limit: 5000MS Memory Limit: 131072K
    Total Submissions: 142057 Accepted: 44088
    Case Time Limit: 2000MS

    Description

    You have N integers, A1A2, ... , AN. You need to deal with two kinds of operations. One type of operation 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.

    Input

    The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
    The second line contains N numbers, the initial values of A1A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
    Each of the next Q lines represents an operation.
    "C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
    "Q a b" means querying the sum of AaAa+1, ... , Ab.

    Output

    You need to answer all Q commands in order. One answer in a line.

    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

    Hint

    The sums may exceed the range of 32-bit integers.
  • 题解1:先考虑利用线段树,每个节点维护两个数据:①给这个节点对应的区间内的所有元素共同加上的值 ②在这个节点对应的区间中除去①之外其它的值的和。通过单独维护共同加上的值,给区间同时加一个值的操作就可以高效地进行了,如果对于父亲节点同时加了一个值,那么这个值就不会在儿子节点被重复考虑。在递归计算和时再把这一部分的值加到结果里面就可以了。这样不论是同时加一个值还是查询一段的和复杂度都是O(log n).
  • 代码1:
     1 #include <cstdio>
     2 #include <cctype>
     3 #include <cmath>
     4 #define number s-'0'
     5 
     6 using namespace std;
     7 
     8 const int MAX_N=100000;
     9 const int MAX_Q=100000;
    10 const int DAT_SIZE=(1<<18)-1;
    11 int N,Q;
    12 int A[MAX_N];
    13 char T[MAX_Q];
    14 int L[MAX_Q], R[MAX_Q], X[MAX_Q];
    15 long long data[DAT_SIZE], datb[DAT_SIZE];
    16 
    17 void read(int &x){
    18     char s;
    19     x=0;
    20     bool flag=0;
    21     while(!isdigit(s=getchar()))
    22         (s=='-')&&(flag=true);
    23     for(x=number;isdigit(s=getchar());x=x*10+number);
    24     (flag)&&(x=-x);
    25 }
    26 
    27 void write(int x)
    28 {
    29     if(x<0)
    30     {
    31         putchar('-');
    32         x=-x;
    33     }
    34     if(x>9)
    35         write(x/10);
    36     putchar(x%10+'0');
    37 }
    38 
    39 int min(int x, int y)
    40 {
    41     if (x<y) return x;
    42     return y;
    43 }
    44 
    45 int max(int x, int y)
    46 {
    47     if (x>y) return x;
    48     return y;
    49 }
    50 
    51 void solve();
    52 void add(int a, int b, int x, int k, int l, int r);
    53 long long sum(int a, int b, int k, int l, int r); 
    54 
    55 int main()
    56 {
    57     read(N);read(Q);
    58     for (int i=0; i<N; i++) read(A[i]);
    59     for (int i=0; i<Q; i++)
    60     {
    61         scanf("%c", &T[i]);
    62         if (T[i]=='Q') {read(L[i]);read(R[i]);}
    63         else {read(L[i]);read(R[i]);read(X[i]);}
    64     }
    65     solve();
    66 }
    67 
    68 void solve()
    69 {
    70     for (int i=0; i<N; i++) add(i, i+1, A[i], 0, 0, N);
    71     for (int i=0; i<Q; i++)
    72         if (T[i]=='C') add(L[i]-1, R[i], X[i], 0, 0, N);
    73         else printf("%lld\n", sum(L[i]-1, R[i], 0, 0, N));
    74 }
    75 
    76 void add(int a, int b, int x, int k, int l, int r)
    77 {
    78     if (a<=l && r<=b) data[k]+=x;
    79     else if (l<b && r>a)
    80     {
    81         datb[k]+=(min(b,r)-max(a,l))*x;
    82         add(a, b, x, k*2+1, l, (l+r)/2);
    83         add(a, b, x, k*2+2, (l+r)/2, r);
    84     }
    85 }
    86 
    87 long long sum(int a, int b, int k, int l, int r)
    88 {
    89     if (b<=l || a>=r) return 0;
    90     else if(l>=a && r<=b) return data[k]*(r-l)+datb[k];
    91     else 
    92     {
    93         long long res=(min(b,r)-max(a,l))*data[k];
    94         res+=sum(a, b, k*2+1, l, (l+r)/2);
    95         res+=sum(a, b, k*2+2, (l+r)/2, r);
    96         return res;
    97     }
    98 }

     

  • 题解2:树状数组也可以通过在每个节点上维护两个数据,高效地进行上述操作。如果给区间[l,r]同时加上x的话,考虑每个节点的值会如何变化。令s(i)是加上x之前的前缀和,s'(i)是加上x之后的前缀和。那么就有:
    i<l→s'(i)=s(i)
    l≤i≤r→s'(i)=s(i)+x*(i-l+1)
                    =s(i)+x*i-x*(l-1)
    r<i→s'(i)=s(i)+x*(r-l+1)
    下面记sum(bit,i)为树状数组bit的前i项和。我们构建两个树状数组bit0和bit1,并且设∑(j=1,i)aj=sum(bit1,i)*i+sum(bit0,i)
    那么在[l,r]区间上同时加上x就可以看成是
    1.在bit0的l位置加上-x(l-1)
    2.在bit1的l位置加上x
    3.在bit0的r+1位置加上xr
    4.在bit1的r+1位置加上-x
    这4个操作。因此查询和更新操作都可以在O(log n)时间里完成。
    更一般地,如果操作得到的结果可以用i的n次多项式表示,那么就可以使用n+1个树状数组来进行维护了。
  • 代码2:
     1 #include <cstdio>
     2 #include <cctype>
     3 #include <cmath>
     4 #define number s-'0'
     5 
     6 using namespace std;
     7 
     8 const int MAX_N=100000;
     9 const int MAX_Q=100000;
    10 int N,Q;
    11 int A[MAX_N+1]; 
    12 char T[MAX_Q];
    13 int L[MAX_Q], R[MAX_Q], X[MAX_Q];
    14 long long bit0[MAX_N+1], bit1[MAX_N+1];
    15 
    16 
    17 void read(int &x){
    18     char s;
    19     x=0;
    20     bool flag=0;
    21     while(!isdigit(s=getchar()))
    22         (s=='-')&&(flag=true);
    23     for(x=number;isdigit(s=getchar());x=x*10+number);
    24     (flag)&&(x=-x);
    25 }
    26 
    27 void write(long long x)
    28 {
    29     if(x<0)
    30     {
    31         putchar('-');
    32         x=-x;
    33     }
    34     if(x>9)
    35         write(x/10);
    36     putchar(x%10+'0');
    37 }
    38 
    39 long long sum(long long *b, int i)
    40 {
    41     long long s=0;
    42     while (i>0)
    43     {
    44         s+=b[i];
    45         i=i&(i-1);
    46     }
    47     return s;
    48 }
    49 
    50 void add(long long *b, int i, int v)
    51 {
    52     while (i<=N)
    53     {
    54         b[i]+=v;
    55         i+=i&-i;
    56     }
    57 }
    58 
    59 void solve();
    60 
    61 int main()
    62 {
    63     read(N);read(Q);
    64     for (int i=1; i<=N; i++) read(A[i]);
    65     for (int i=0; i<Q; i++)
    66     {
    67         scanf("%c", &T[i]);
    68         if (T[i]=='Q') {read(L[i]);read(R[i]);}
    69         else {read(L[i]);read(R[i]);read(X[i]);}
    70     }
    71     solve();
    72 }
    73 
    74 void solve()
    75 {
    76     for (int i=1; i<=N; i++)
    77     {
    78         add(bit0, i, A[i]);
    79     }
    80     for (int i=0; i<Q; i++)
    81     {
    82         if (T[i]=='C')
    83         {
    84             add(bit0, L[i], -X[i]*(L[i]-1));
    85             add(bit1, L[i], X[i]);
    86             add(bit0, R[i]+1, X[i]*R[i]);
    87             add(bit1, R[i]+1, -X[i]);
    88         }
    89         else
    90         {
    91             long long res=0;
    92             res+=sum(bit0, R[i])+sum(bit1, R[i])*R[i];
    93             res-=sum(bit0, L[i]-1)+sum(bit1, L[i]-1)*(L[i]-1);
    94             printf("%lld\n", res);
    95         }
    96     }
    97 }

     

转载于:https://www.cnblogs.com/Ymir-TaoMee/p/9544756.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值