Time Limit: 1 Sec Memory Limit: 512 MB
Submit: 1639 Solved: 728
Description
Input
第一行给出两个整数N,M。分别表示序列长度和操作个数
接下来一行有N个数,即给定的序列a1,a2,….an
接下来M行,每行对应一个操作,格式见题目描述
Output
对于每个询问操作,输出一行,表示所询问的SSi的值。
Sample Input
5 3
1 2 3 4 5
Query 5
Modify 3 2
Query 5
Sample Output
35
32
HINT
1<=N,M<=100000,且在任意时刻0<=Ai<=100000
Source
Katharon+#1
这题看起来有点奇怪,貌似需要维护的东西不能直接求的,但是其实我们可以间接地求,以后要学会这种转化的思路,拿着式子多多推理一下,而不是直接去看题解,说不定就捣鼓出来了呢
S表示前缀和,则
S=a1,a1+a2,a1+a2+a3....
SS表示前缀和的前缀和
SS=a1,2∗a1+a2,3∗a1+2∗a2+a1...
则把每个S的值全部乘以i之后,令新的数组为S’
则
S′=a1,2∗a1+2∗a2,3∗a1+3∗a2+3∗a3...
用S’减去SS会发现一个很神奇的效果,令新数组为SS’
则
SS′=0∗a1,0∗a1+1∗a2,0∗a1+1∗a2+2∗a3...
我们发现又是升序的有规律的有前缀和的性质的东西了,所以我们只需要维护SS’数组和原来的数组S,最后减回去就是SS数组了
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int n,m;
const int MAXN = 100000 + 100;
long long a[MAXN], C1[MAXN], C2[MAXN];
long long query_fir( int x ) { long long tmp = 0; while( x ) { tmp += C1[x]; x -= x & (-x); } return tmp; }
long long query_sec( int x ) { long long tmp = 0; while( x ) { tmp += C2[x]; x -= x & (-x); } return tmp; }
void modify_fir( int x, long long num ) { while( x <= n ) { C1[x] += num; x += x & (-x); } }
void modify_sec( int x, long long num ) { while( x <= n ) { C2[x] += num; x += x & (-x); } }
int main( ) {
scanf( "%d%d", &n, &m );
for( register int i = 1; i <= n; i++ ) {
scanf( "%lld", &a[i]);
modify_fir( i, a[i] );
modify_sec( i, ( i - 1 ) * a[i] );
}
for( register int i = 1; i <= m; i++ ) { char ch[10]; int x;
scanf( "%s", ch );
if( ch[0] == 'Q' ) {
scanf( "%d", &x );
printf( "%lld\n", x * query_fir( x ) - query_sec( x ) );
} else {
long long y;
scanf( "%d%lld", &x, &y );
long long tmp = y - a[x];
a[x] += tmp;
modify_fir( x, tmp );
modify_sec( x, ( x - 1 ) * tmp );
}
}
return 0;
}