SPOJ 11840. Sum of Squares with Segment Tree (线段树,区间更新)

http://www.spoj.com/problems/SEGSQRSS/

SPOJ Problem Set (classical)

11840. Sum of Squares with Segment Tree

Problem code: SEGSQRSS

Segment trees are extremely useful.  In particular "Lazy Propagation" (i.e. see
here, for example
) allows one to compute sums over a range in O(lg(n)), and update ranges in O(lg(n)) as well.  In this problem you will compute something much harder:

The sum of squares over a range with range updates of 2 types:

1) increment in a range

2) set all numbers the same in a range.

Input

There will be T (T <= 25) test
cases in the input file.  First line of the input contains two positive integers, N (N <= 100,000) and Q
(Q <= 100,000)
. The next line contains N integers,
each at most 1000.  Each of the next Qlines
starts with a number, which indicates the type of operation:

st nd  -- return the sum of the squares of the numbers with
indices in [st, nd]
{i.e., from st to nd inclusive} (1
<= st <= nd <= N).

st nd x -- add "x" to all numbers with indices in [st, nd] (1
<= st <= nd <= N, and
-1,000 <= x <= 1,000).

st nd x -- set all numbers with indices in
[st, nd] to
"x" (1 <= st <= nd <= N, and
-1,000 <= x <= 1,000).

Output

For each test case output the “Case <caseno>:” in
the first line and from the second line output the sum of squares for each operation of type 2.  Intermediate overflow will not occur
with proper use of 64-bit signed integer.

Example

Input:

2

4 5

1 2 3 4

2 1 4

0 3 4 1

2 1 4

1 3 4 1

2 1 4

1 1

1

2 1 1

Output:

Case 1:

30

7

13

Case 2:

1

 

Added by: Chen Xiaohong
Date: 2012-07-11
Time limit: 6s
Source limit: 50000B
Memory limit: 256MB
Cluster: Pyramid (Intel Pentium III 733 MHz)
Languages: All

题意:

有三种操作:将区间中的所有数置为x;将区间中的所有数加上x;求区间内所有数的平方和。

分析:

先考虑如果不需要求平方和,只是求和,我们需要维护这些数据:addv-区间内的数共同加上的值;setv-区间内的数都置为的值(setv=INF表示不设置);sumv-区间内的数加上addv之前的值。

但这题求的是平方和,似乎不是很好维护。如果只是set操作,还是很好维护的,那么难点就在于add操作了。考虑如下等式:(x+v)^2=x^2+2xv+v^2,x是add操作之前的数,v是add的数,这是一个数的情况,那么一段区间内的数呢?

显然有sum(xi^2)+(v^2)*length+2*sum(xi)*v,这样问题就迎刃而解了,只要再维护一个区间的平方和就行,当set时直接改,add时加上(v^2)*length+2*sum(xi)*v即可。

/*
 *
 * Author : fcbruce
 *
 * Time : Fri 03 Oct 2014 04:16:10 PM CST
 *
 */
#include <cstdio>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <cctype>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <queue>
#include <list>
#include <vector>
#include <map>
#include <set>
#define sqr(x) ((x)*(x))
#define LL long long
#define itn int
#define INF 0x3f3f3f3f
#define PI 3.1415926535897932384626
#define eps 1e-10

#ifdef _WIN32
  #define lld "%I64d"
#else
  #define lld "%lld"
#endif

#define maxm
#define maxn 100007

using namespace std;

int addv[maxn<<2],setv[maxn<<2];
long long sumv[maxn<<2],sqrsumv[maxn<<2];

inline void pushdown(int k,int l,int r)
{
  int lc=k*2+1,rc=k*2+2,m=l+r>>1;
  addv[lc]+=addv[k];
  addv[rc]+=addv[k];
  addv[k]=0;

  if (setv[k]!=INF)
  {
    setv[lc]=setv[rc]=setv[k];
    sumv[lc]=(LL)setv[lc]*(m-l);sumv[rc]=(LL)setv[rc]*(r-m);
    sqrsumv[lc]=sqr((LL)setv[k]*(m-l));sqrsumv[rc]=sqr((LL)setv[rc])*(r-m);
    addv[lc]=addv[rc]=0;
    setv[k]=INF;
  }
}

inline void pushup(int k,int l,int r)
{
  int lc=k*2+1,rc=k*2+2,m=l+r>>1;

  sumv[k]=sumv[lc]+sumv[rc]+(LL)addv[lc]*(m-l)+(LL)addv[rc]*(r-m);
  sqrsumv[k]=sqrsumv[lc]+sqrsumv[rc]+(LL)(r-l)*(addv[k])+2ll*sumv[k]*addv[k];
}

void build(int k,int l,int r)
{
  addv[k]=0;
  setv[k]=INF;
  sumv[k]=sqrsumv[k]=0ll;

  if (r-l==1)
  {
    scanf("%d",&sumv[k]);
    sqrsumv[k]=sqr((LL)sumv[k]);
    return ;
  }

  build(k*2+1,l,l+r>>1);
  build(k*2+2,l+r>>1,r);

  pushup(k,l,r);
}

void update_add(int a,int b,int v,int k,int l,int r)
{
  if (b<=l || r<=a) return ;
  if (a<=l && r<=b)
  {
    addv[k]+=v;
    sqrsumv[k]+=sqr((LL)v)*(r-l)+2ll*v*sumv[k];
    return ;
  }

  pushdown(k,l,r);

  update_add(a,b,v,k*2+1,l,l+r>>1);
  update_add(a,b,v,k*2+2,l+r>>1,r);

  pushup(k,l,r);
}

void update_set(int a,int b,int v,int k,int l,int r)
{
  if (b<=l || r<=a) return ;
  if (a<=l && r<=b)
  {
    addv[k]=0;
    setv[k]=v;
    sumv[k]=(LL)v*(r-l);
    sqrsumv[k]=sqr((LL)v)*(r-l);
    return ;
  }

  pushdown(k,l,r);

  update_set(a,b,v,k*2+1,l,l+r>>1);
  update_set(a,b,v,k*2+2,l+r>>1,r);

  pushup(k,l,r);
}

long long query(int a,int b,int k,int l,int r)
{
  if (b<=l || r<=a) return 0ll;
  if (a<=l && r<=b) return sqrsumv[k];

  pushdown(k,l,r);

  return query(a,b,k*2+1,l,l+r>>1)+query(a,b,k*2+2,l+r>>1,r);
}

int main()
{
#ifdef FCBRUCE
  freopen("/home/fcbruce/code/t","r",stdin);
#endif // FCBRUCE

  int T_T,__=0;
  scanf("%d",&T_T);

  while (T_T--)
  {
    int n,m;
    scanf("%d%d",&n,&m);

    build(0,0,n);

    printf("Case %d:\n",++__);

    int op,a,b,v;
    while (m--)
    {
      scanf("%d",&op);

      switch (op)
      {
        case 0:
          scanf("%d%d%d",&a,&b,&v);
          a--;
          update_set(a,b,v,0,0,n);
          break;
        case 1:
          scanf("%d%d%d",&a,&b,&v);
          a--;
          update_add(a,b,v,0,0,n);
          break;
        case 2:
          scanf("%d %d",&a,&b);
          a--;
          printf(lld "\n",query(a,b,0,0,n));
          break;
      }
    }
  }

  return 0;
}
时间: 2024-12-23 07:41:25

SPOJ 11840. Sum of Squares with Segment Tree (线段树,区间更新)的相关文章

XTU1238 Segment Tree (线段树&#183;区间最值更新)

对一个数组有四种操作 1: 将区间[ l, r] 中的所有值都加上c 2:将区间 [l, r] 中所有比c大的值改为c 3:将区间 [l, r] 中所有比c小的值改为c 4:输出区间 [l, r] 中所有数的最小值和最大值 对每个操作4输出对应最小值和最大值基础的线段树  在湘潭卡了好久没写出来  线段树维护三个值 区间最大值 maxv, 区间最小值minv, 区间增加的值add  操作1是... http://www.worlduc.com/blog2012.aspx?bid=44692767

HDU 4107 Gangster Segment Tree线段树

这道题也有点新意,就是需要记录最小值段和最大值段,然后成段更新这个段,而不用没点去更新,达到提高速度的目的. 本题过的人很少,因为大部分都超时了,我严格按照线段树的方法去写,一开始居然也超时. 然后修补了两个地方就过了,具体修改的地方请参看程序. 知道最大值段和最小值段,然后修补一下就能过了.不是特别难的题目. #include <stdio.h> #include <string> #include <algorithm> using namespace std; c

BNU 28887——A Simple Tree Problem——————【将多子树转化成线段树+区间更新】

A Simple Tree Problem Time Limit: 3000ms Memory Limit: 65536KB This problem will be judged on ZJU. Original ID: 368664-bit integer IO format: %lld      Java class name: Main Prev Submit Status Statistics Discuss Next Type: None None Graph Theory 2-SA

SPOJ GSS3 Can you answer these queries III (线段树)

题目大意: 求区间最大子区间的和. 思路分析: 记录左最大,右最大,区间最大. 注意Q_L  和 Q_R  就好. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define lson num<<1,s,mid #define rson num<<1|1,mid+1,e #define maxn 55555 using na

【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)

[BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少.1 ≤ n ≤ 105,1 ≤ m ≤ 105,1 ≤ l ≤ r ≤ n, 1 ≤ k ≤ 20 Sample Input 9 9 -8 9 -1 -1 -1 9 -8 9 3 1 1 9 1 1 1 9 2 1 4 6 3 Sample Output 17 25

SPOJ GSS4 Can you answer these queries IV (线段树)

题目大意: 给出N个数 0     操作   把 l -----  r之间的数全部开平方 1     操作  输出 l -----r  之间的和 思路分析: 判断区间里的数字是否全相同.如果相同, 将cov 置为该数 查询的时候和更新的时候,如果碰到cov != -1 的  就直接返回就可以了 #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #incl

【bzoj3638】Cf172 k-Maximum Subsequence Sum 模拟费用流+线段树区间合并

题目描述 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少. 输入 The first line contains integer n (1 ≤ n ≤ 105), showing how many numbers the sequence has. The next line contains n integers a1, a2, ..., an (|ai| ≤ 500). The third line contain

2-D range sum query implementation(2-D segment tree)

Google interview question:一个二维数组,有两个方法,一个是update(x,y),更新一个cell的值,一个是query(x1,y1,x2,y2),查询(x1,y1,x2,y2)矩形内所有元素的和. Senario 1. update调用次数远大于query. Senario 2. query调用次数远大于update. Senario 3. 两种方法调用一样多. 对于senario 1,只需要用一个二维数组储存数据,update相应的cell,时间复杂度O(1),qu

SPOJ 375 QTREE系列-Query on a tree (树链剖分)

题目地址:SPOJ 375 树链剖分第一发! 果然是个貌似很高级的数据结构,其实就是把树的边从树形结构转化成了线性结构,从而可以用线段树或树状数组之类的数据结构进行快速维护.从而将时间缩到n*log(2*n). 这题用的线段树维护的. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #incl