A Simple Problem with Integers
Time Limit: 5000MS   Memory Limit: 131072K
Total Submissions: 96635   Accepted: 30152
Case Time Limit: 2000MS

Description

You have N integers, A1, A2, ... , 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 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.

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.

【参考代码即模板】 cxlove神
【解题方法】为了练习splay,这题强行splay写了。都是splay的典型操作了,没什么说的,不过这效率比用线段树慢了3倍。代码长度也长了3倍。所以什么题用什么数据结构还是要选择好,等到写t了就来不及了。线段树的ac代码我以前写过。
【splay AC 代码】
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define Key_value ch[ch[root][1]][0]
const int inf=0x3f3f3f3f;
const int maxn=100005;
typedef long long LL;
/********************非结构体版本**************************/
int pre[maxn],key[maxn],ch[maxn][2],root,tot1;//分别表示父结点,键值,左右孩子(0为左孩子,1为右孩子),根结点,结点数量
int size[maxn],s[maxn],tot2,val[maxn];//分别表示子树规模,内存池,内存池容量
int add[maxn];//延迟标记
LL sum[maxn];//子树节点和
int n,q;
//debug部分copy from hh
void Treaval(int x) {
    if(x) {
        Treaval(ch[x][0]);
        printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,val = %2d , sum = %2d \n",x,ch[x][0],ch[x][1],pre[x],size[x],val[x],sum[x]);
        Treaval(ch[x][1]);
    }
}
void debug() {printf("%d\n",root);Treaval(root);}
//以上Debug
void newnode(int &r,int father,int k)
{
    if(tot2) r=s[--tot2];
    else r=++tot1;
    pre[r]=father;
    val[r]=k;
    sum[r]=k;
    add[r]=0;
    size[r]=1;
    ch[r][0]=ch[r][1]=0;//左右儿子为空
}
//将延迟标记更新到孩子节点
void pushdown(int rt)
{
    if(add[rt])
    {
        val[rt]+=add[rt];
        add[ch[rt][0]]+=add[rt];
        add[ch[rt][1]]+=add[rt];
        sum[ch[rt][0]]+=(1LL)*add[rt]*size[ch[rt][0]];
        sum[ch[rt][1]]+=(1LL)*add[rt]*size[ch[rt][1]];
        add[rt]=0;
    }
}
//通过孩子节点更新父亲节点
void pushup(int rt)
{
    size[rt]=size[ch[rt][0]]+size[ch[rt][1]]+1;
    sum[rt]=sum[ch[rt][0]]+sum[ch[rt][1]]+val[rt]+add[rt];
}
//旋转,kind为1表示左旋,kind为0表示右旋
void Rotate(int x,int kind)
{
    int y=pre[x];
    pushdown(x);
    pushdown(y);
    //类似SBT,要把其中一个分支先给父节点
    ch[y][!kind]=ch[x][kind];
    pre[ch[x][kind]]=y;
    //如果父节点不是根节点,则要和父节点的父节点连接起来
    if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x;
    pre[x]=pre[y];
    ch[x][kind]=y;
    pre[y]=x;
    pushup(y);
}
//Splay 调整,将根为r的子树调整为goal
void Splay(int r,int goal)
{
    pushdown(r);
    while(pre[r]!=goal)
    {
        //父节点即是目标位置,goal为0表示,父节点就是根节点
        if(pre[pre[r]]==goal)
            Rotate(r,ch[pre[r]][0]==r);
        else{
            int y=pre[r];
            int kind=ch[pre[y]][0]==y;
            //两个方向不同,则先左旋再右旋
            if(ch[y][kind]==r)
            {
                Rotate(r,!kind);
                Rotate(r,kind);
            }
            //两个方向相同,相同方向旋转两次
            else{
                Rotate(y,kind);
                Rotate(r,kind);
            }
        }
    }
    pushup(r);
    //更新根节点
    if(goal==0) root=r;
}
//把第k位的数转到goal下边
void RotateTo(int k,int goal)
{
    int r=root;
    pushdown(r);
    while(size[ch[r][0]]!=k)
    {
        if(k<size[ch[r][0]])
        {
            r=ch[r][0];
        }
        else
        {
            k-=(size[ch[r][0]]+1);
            r=ch[r][1];
        }
        pushdown(r);
    }
    Splay(r,goal);
}
//插入值
int Insert(int k)
{
    int r=root;
    while(ch[r][key[r]<k])
        r=ch[r][key[r]<k];
    newnode(ch[r][k>key[r]],r,k);
    //将新插入的节点更新至根节点
    Splay(ch[r][k>key[r]],0);
    return 1;
}
//找前驱,即左子树的最右节点
int get_pre(int x)
{
    int tmp=ch[x][0];
    if(tmp==0) return inf;
    while(ch[tmp][1]) tmp=ch[tmp][1];
    return key[x]-key[tmp];
}
//找后继,即右子树的最左节点
int get_next(int x)
{
    int tmp=ch[x][1];
    if(tmp==0) return inf;
    while(ch[tmp][0]) tmp=ch[tmp][0];
    return key[tmp]-key[x];
}
//查询L,R区间的和
LL queryans(int l,int r)
{
    RotateTo(l-1,0);
    RotateTo(r+1,root);
    return sum[Key_value];
}
//更新
void Update(int l,int r)
{
    int k;
    scanf("%d",&k);
    RotateTo(l-1,0);
    RotateTo(r+1,root);
    add[Key_value]+=k;
    sum[Key_value]+=size[Key_value]*k;
}
int a[maxn];
//建树,中间节点先建立,然后分别对区间两端在左右子树建立
void BuildTree(int &x,int l,int r,int father)
{
    if(l>r) return ;
    int mid=(l+r)/2;
    newnode(x,father,a[mid]);
    if(l<mid) BuildTree(ch[x][0],l,mid-1,x);
    if(r>mid) BuildTree(ch[x][1],mid+1,r,x);
    pushup(x);
}
void init()
{
    for(int i=0; i<n; i++) scanf("%d",&a[i]);
    ch[0][0]=ch[0][1]=pre[0]=size[0]=0;
    add[0]=sum[0]=0;
    root=tot1=0;
    newnode(root,0,-1);
    newnode(ch[root][1],root,-1);//头尾各加一个空位
    size[root]=2;
    BuildTree(Key_value,0,n-1,ch[root][1]);//让所有数据夹在两个-1之间
    pushup(ch[root][1]);
    pushup(root);
}

int main()
{
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        init();
        while(q--)
        {
            char op[10];
            int x,y;
            scanf("%s%d%d",op,&x,&y);
            if(op[0]=='Q') printf("%lld\n",queryans(x,y));
            else Update(x,y);
        }
    }
    return 0;
}