次数 | a1 a 1 | a2 a 2 | a3 a 3 | a4 a 4 | a5 a 5 | 通项 |
---|---|---|---|---|---|---|
零次 | 1 | 1 | 1 | 1 | 1 | an=1 a n = 1 |
一次 | 1 | 2 | 3 | 4 | 5 | an=n a n = n |
二次 | 1 | 3 | 6 | 10 | 15 | an=n(n+1)2 a n = n ( n + 1 ) 2 |
三次 | 1 | 4 | 10 | 20 | 35 | an=n(n+1)(n+2)6 a n = n ( n + 1 ) ( n + 2 ) 6 |
…. |
要将区间加上一个1, 4, 9, 16…即平方数序列, 每次直接操作的复杂度都是区间长度, 如果有很多次这样的操作, 就必须利用差分标记了
因为
n2=2n(n+1)2−n
n
2
=
2
n
(
n
+
1
)
2
−
n
所以可以用差分标记, 加上两次{1, 3, 6, 10…}, 变成{2, 6, 12, 20…}, 再减去{1, 2, 3, 4…}, 变成{1, 4, 9, 16…}
如果有多组操作, 可以用差分标记, 将所有操作标记起来, 最后只需要计算常数次前缀和就好了
如果要加上其他形式的高阶多项式, 也可以由上面的若干个通项组成
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn = 1e6+100;
ll a[maxn];
ll op, l, r, x;
int n;
ll add[3][maxn];
void show()
{
for(int i=1; i<=n; ++i) cout << a[i] << " ";
cout << endl;
}
void cal()
{
for(int i=1; i<=3; ++i)
{
for(int j=0; j<i; ++j)
{
for(int k=2; k<=n; ++k)
{
add[i-1][k] += add[i-1][k-1];
}
}
}
for(int i=1; i<=n; ++i)
{
for(int j=0; j<3; ++j) a[i] += add[j][i];
}
}
void add_0(ll l, ll r, ll x)//x
{
add[0][l] += x;
add[0][r+1] -= x;
}
void add_1(ll l, ll r, ll x)//nx
{
int n = (r-l+1);
add[1][l] += x;
add[1][r+1] -= x;
add[1][r+1] -= x*n;
add[1][r+2] += x*n;
}
void add_2(ll l, ll r, ll x)//n*(n+1)/2*x
{
int n = (r-l+1);
add[2][l] += x;
add[2][r+1] -= x;
add[2][r+1] -= x*n;
add[2][r+2] += x*n;
add[2][r+1] -= x*n*(n+1)/2;
add[2][r+2] += x*n*(n+1)/2;
add[2][r+2] += x*n*(n+1)/2;
add[2][r+3] -= x*n*(n+1)/2;
}
int main()
{
// cin >> n;
n = 20;
add_2(1, 10, 2);
add_1(1, 10, -1);
cal();
show();
return 0;
}