前缀和
一维前缀和
- 作用:快速查询数组a[l]~a[r]的和,时间复杂度为o(1)。
- 求法:例如数组a[],用一个数组sum[]来记录它的前n项和,例如sum[n]表示a[1]+a[2]+...+a[n]。
-
1 int sum[1000]; 2 void init() 3 { 4 for(int i=1; i<=n; i++) 5 sum[i]=sum[i-1]+a[i]; 6 } 7 int query(int l, int r) 8 { 9 return sum[r]-sum[l-1]; 10 }
加一道前缀和的骚题。。。
这道题居然可以用前缀和来解决,没想到。。。
我们可以枚举自然数段的长度为i
当i为2时,有 12 23 45 67
为i3时有123 234 345 456
为i4时有1234 2345 3456 4567
我们可以发现这些串就可以表示任意一段的区间的和了, 然后奇妙的事发生了, 当要表示长度为i的区间的和时只需要用到第一列的数据和,然后在它的基础上加上k倍的i即可表示
即设前缀和为a[n], 设要表示的数据和为n,如果存在一个区间和能表示这个数, 那么对应存在一个k满足n=sum[i]+k*i;然后上代码。
1 #include<cstdio>
2 long long a[2000005];
3 int main()
4 {
5 int n;
6 scanf("%d", &n);
7 for(int i=1; i<=2000000; i++)
8 {
9 a[i]=a[i-1]+i;
10 }
11 for(int i=2000000; i>=2; i--)
12 {
13 if(n>=a[i] && (n-a[i])%i==0)
14 {
15 printf("%d %d\n", 1+(n-a[i])/i, (n-a[i])/i+i);
16 }
17 }
18 }
二维前缀和
- 作用:快速查询数组a[][]任意二维区间的和
- 求法:用一个数组sum[n][m]表示所有a[n'][m']的和(1<=n'<=n,1<=m'<=m)。
- 下面代码的例图。
-
1 int sum[1000][1000]; 2 void init() 3 { 4 for(int i=1; i<=n; i++) 5 for(int j=1; j<=m; j++) 6 sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+a[i][j]; 7 } 8 int query(int x1, int y1, int x2, int y2) 9 { 10 return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]; 11 }
一维差分
- 作用:设数组为a[],对多次a数组上的区间[l,r]实现加减val的操作(将对区间的的操作转化为对两个端点的操作)。
- 做法:
1 int a[n]; 2 int d[n]; 3 int c[n]; 4 void init(int l, int r, int val) 5 { 6 d[l]+=val;d[r+1]-=val; 7 } 8 for(int i=1; i<=n; i++) 9 { 10 c[i]=c[i-1]+d[i]; 11 } 12 for(int i=1; i<=n; i++) 13 { 14 a[i]+=c[i]; 15 }
二维差分
-
1 void init(int x1, int y1, int x2, int y2, int val) 2 { 3 sum[x1][y1] += val; 4 sum[x1][y2 + 1] -= val; 5 sum[x2 + 1][y1] -= val; 6 sum[x2 + 1][y2 + 1] += val; 7 } 8 9 void get() 10 { 11 for(int i = 1; i <= n; i++) 12 for(int j = 1; j <= n; j++) 13 sum[i][j] += sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1]; 14 }