题目链接: 小w的糖果
大致题意
有一个长度为
n
n
n的数组, 初始情况数组内所有的元素均为
0
0
0, 进行
m
m
m次如下三种操作:
① 给
[
p
o
s
,
n
]
[pos, n]
[pos,n]区间所有元素+1
② 给
[
p
o
s
,
n
]
[pos, n]
[pos,n]区间的所有元素加上一个首项为1, 公差也为1的等差数列.
③ 给
[
p
o
s
,
n
]
[pos, n]
[pos,n]区间的所有元素加上一个
1
,
4
,
9
,
16
,
.
.
.
1, 4, 9, 16, ...
1,4,9,16,...的平方数列.
最终输出序列中每个位置的值 m o d 1 0 9 + 7 mod \ 10^9 + 7 mod 109+7.
解题思路
注: 下文所提到的等差数列均指首项和公差均为1的等差数列.
我们发现题目中实际上是在进行一系列的区间加操作, 最终再输出序列.
我们比较容易想到用差分的方式来维护序列.
但是我们区间加操作, 只能对于一个区间加上相同的数字, 是不能直接加等差数列 和 平方数列的.
现在有等差数列 1 2 3 4 5 6 7
我们求出其差分数组: 1 1 1 1 1 1 1
再次对差分数组求差分: 1 0 0 0 0 0 0
这相当于什么?
如果我们假设差分数组为b[]
如果我想给[1, 7]加上一个等差数列, 我只需要在b[1]的位置加上1, b[8]的位置减去1.
然后我对b数组进行求前缀和, 此时我们就能得到一个{ 1 1 1 1 1 1 1 }序列.
然后再求一次前缀和, 我们就得到了{ 1 2 3 4 5 6 7 }序列.
因此我们的出结论: 如果我想给一个序列加上一个等差数列, 我们可以通过对差分数组求两次前缀和的方式得到.
同理我们考虑对于序列加平方数列的操作
原序列: 1 4 9 16 25 36 49
一次差分: 1 3 5 7 9 11 13
两次差分: 1 2 2 2 2 2 2
三次差分: 1 1 0 0 0 0 0
这又相当于什么?
如果我们假设差分数组为c[]
如果我想给[1, 7]加上一个平方数列, 我们需要对于c[1]加1, c[2]加1, 然后对于c[8]的位置-2
最后求三次前缀和即可.
因此我们同样的出结论: 如果我想给一个序列加上一个平方数列, 我们可以通过对差分数组求三次前缀和的方式得到.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, mod = 1E9 + 7;
int a[N], b[N], c[N]; //分别对应三种操作的差分数组.
void fact(int a[], int n) { rep(i, n) a[i] = (a[i - 1] + a[i]) % mod; }
int main()
{
int t; cin >> t;
while (t--) {
int n, m; scanf("%d %d", &n, &m);
rep(i, n + 1) a[i] = b[i] = c[i] = 0;
rep(i, m) {
int tp, pos; scanf("%d %d", &tp, &pos);
if (tp == 1) a[pos]++, a[n + 1]--;
else if (tp == 2) b[pos]++, b[n + 1]--;
else c[pos]++, c[pos + 1]++, c[n + 1] -= 2;
}
fact(a, n);
rep(i, 2) fact(b, n);
rep(i, 3) fact(c, n);
rep(i, n) printf("%d%c", (0ll + a[i] + b[i] + c[i]) % mod, " \n"[i == n]);
}
return 0;
}