A Simple Problem with Integers
Time Limit: 5000MS | Memory Limit: 131072K | |
Total Submissions: 139870 | Accepted: 43323 | |
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.
这个用线段树很好写,用伸展树写有一点麻烦,但思想是一样的,用个lazy标记就行了,注意打上标记以后要先改子区间的值,不然会wa
#include <algorithm>
#include <cstdio>
#include <iostream>
typedef long long ll;
using namespace std;
ll n;
ll sum[100005];
const ll inf = (ll)1e15;
struct tree
{
ll father,lch,rch,size,val; //父节点、左儿子、右儿子、子树大小、值
}stree[100005];
ll lazy[100005]; // lazy标记
ll root; // 根节点,树的大小
ll a[100005];
inline bool get(ll x) // 判断是父亲的左儿子还是右儿子
{
return stree[ stree[x].father ].rch == x;
}
inline void update(ll x) // 更新子树大小
{
stree[x].size = stree[ stree[x].lch ].size + stree[ stree[x].rch ].size + 1;
sum[x] = sum[stree[x].lch] + sum[stree[x].rch] + stree[x].val;
}
void pushdown(ll x)
{
if (lazy[x]) // 如果当前节点被标记过
{
ll lch = stree[x].lch;
ll rch = stree[x].rch;
stree[lch].val += lazy[x];
stree[rch].val += lazy[x];
sum[rch] += lazy[x] * stree[rch].size;
sum[lch] += lazy[x] * stree[lch].size;//更新区间和
lazy[lch] += lazy[x];
lazy[rch] += lazy[x]; //标记左右儿子节点
lazy[x] = 0; //取消当前节点的标记
}
}
ll build(ll l, ll r, ll rt)
{
if (l > r)
return 0;
ll mid = (l + r) / 2;
ll lch = build(l, mid - 1, mid); //创建左子树
ll rch = build(mid + 1, r, mid); //创建右子树
stree[mid].father = rt;
stree[mid].val = a[mid];
stree[mid].size = 1;
stree[mid].lch = lch;
stree[mid].rch = rch; // 创建该节点
update(mid); // 更新mid节点
return mid;
}
inline void rotate(ll x)//旋转
{
pushdown( stree[x].father ); // 更新x的父节点的lazy标记
pushdown(x); // 更新x的lazy标记
ll fa,oldf,whichx;
fa = stree[x].father; // x的父节点
oldf = stree[fa].father; // x父节点的父节点
whichx = get(x); // 判断x是父节点左儿子还是右儿子
if (whichx) //x是父节点的右儿子
{
stree[fa].rch = stree[x].lch;
stree[ stree[fa].rch ].father = fa; // 将x的左儿子变为x父节点的右儿子
stree[x].lch = fa;
stree[fa].father = x;
stree[x].father = oldf; // 将x父节点变为x左儿子
}
else // 同上
{
stree[fa].lch = stree[x].rch;
stree[ stree[fa].lch ].father = fa;
stree[x].rch = fa;
stree[fa].father = x;
stree[x].father = oldf;
}
if (oldf) // 如果有x的父节点有父节点,则更新祖父节点的(左/右)儿子为x
{
if (stree[oldf].rch == fa)
stree[oldf].rch = x;
else
stree[oldf].lch = x;
}
update(x);
update(fa);// 更新x与x父节点的大小
}
inline void splay(ll x, ll t)//将x伸展到t的子节点
{
pushdown(x);
ll fa = stree[x].father; //将x伸展至父节点
while (fa != t)
{
if (stree[fa].father != t) // 如果父节点不是目的节点
{
if (get(x) == get(fa)) // 一字型(需旋转父节点再旋转x)
rotate(fa);
else
rotate(x); // 之字形(需旋转x两次)
}
rotate(x); // 旋转x
fa = stree[x].father; //更新父节点
}
if (!t)
root = x; // 更新根节点
}
ll find(ll x) //查找x为第几小的值
{
ll u = root;
while (1)
{
if (x <= stree[stree[u].lch].size) // 如果x在当前节点本身或者当前节点的左子树内
u = stree[u].lch;
else // 如果x在当前节点的右子树内
{
x -= stree[stree[u].lch].size;
if (x == 1) // 恰好是当前节点
return u;
x--; // 减去当前节点的出现次数
u = stree[u].rch; // 更新当前节点
}
}
}
void init()
{
a[1] = a[n + 2] = inf; // 两端的标记
root = build(1, n + 2, 0); //建树
}
void adddd(ll l, ll r, ll ad) // 加
{
ll x = find(l);
ll y = find(r + 2);
splay(x, 0);
splay(y, x);
lazy[stree[y].lch] += ad;
sum[stree[y].lch] += ad * stree[stree[y].lch].size;
stree[stree[y].lch].val += ad;
}
ll querysum(ll l, ll r) //求和
{
ll x = find(l);
ll y = find(r + 2);
splay(x, 0);
splay(y, x);
return sum[stree[y].lch];
}
int main()
{
// freopen("in.txt", "r", stdin);
ll q,l,r;
ll cnt;
char c;
scanf("%lld%lld", &n, &q);
for (ll i = 2; i <= n + 1; i++)
scanf("%lld", &a[i]);
init();
while (q--)
{
scanf(" %c", &c);
if (c == 'C')
{
scanf("%lld %lld %lld", &l, &r, &cnt);
adddd(l, r, cnt);
}
else
{
scanf("%lld %lld", &l, &r);
printf("%lld\n", querysum(l, r));
}
}
return 0;
}