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;
}