jzoj5922. 【NOIP2018模擬10.23】sequence(組合數)
5922. 【NOIP2018模擬10.23】sequence
Description
小 F 是一位 Hack 國的居民,他生活在一條長度為 n 的街道上,這個街道上總共有 n 個商店。每個商店裡售賣著不同的 Hack 技能包,每個商店本身也會有個便利值。初始時,每個商店的便利值均為 0。每一天,街道上都會有一些商店優化改造。
具體來說,對於每一天,優化改造的商店都是一個連續的區間 l ∼ r,每次優化改造也會有一個優化引數 k。對於所有 l ≤ i ≤ r ,第 i 個商店的便利值會增加 。
小 F 想知道,m 天之後,每個商店的便利值分別是多少。由於小 F 並不喜歡高精度,因此你只需要輸出便利值對 10^9 + 7 取模的結果。
Input
從檔案sequence.in中讀入資料。
第 1 行,兩個整數 n, m 表示街道的長度與天數。
接下來的 m 行,每行三個整數 l, r, k,表示第 i 天優化改造的商店區間和優化引數。
Output
輸出到檔案sequence.out中,共 n 行。
每行 1 個整數,表示第 i 個商店的便利值對 109 + 7 取模的結果。
Sample Input
5 3
1 4 3
2 5 0
3 4 2
Sample Output
1
5
12
24
1
第 1 次操作之後,每個商店的便利值分別為 1, 4, 10, 20, 0。
第 2 次操作之後,每個商店的便利值分別為 1, 5, 11, 21, 1。
第 3 次操作之後,每個商店的便利值分別為 1, 5, 12, 24, 1。
Data Constraint
對於 100% 的資料,滿足 1 ≤ n, m ≤ 5 × 10^5, 0 ≤ k ≤ 20。除此之外,對於每個資料點,還滿足以下限制。
分析:可以發現組合數有一個簡單的性質,即C(n,m)=C(n-1,m)+C(n-1,m-1),我們可以從這個式子中獲得啟發。考慮一個下標從 0 開始的數列,這個數列的每個數均為 1。
我們對這個數列做 k 階字首和。通過這個簡單的式子,容易發現這個數列的第 i項即為C(k+i,k)。於是我們可以得出一個這樣的做法。
對於這個數列,維護這個數列的 k 階差分。對於一次修改操作,我們只需要在各階差分陣列上修改,最後做一遍 k 階的字首和即可。注意差分陣列上修改時,區間邊界要相應地減掉一些值。
程式碼
#include <cstdio>
#include <algorithm>
#define N 600000
#define ll long long
#define mo 1000000007
using namespace std;
ll c[N][30],f[N][30];
int n,m;
int main()
{
freopen("sequence.in", "r", stdin);
freopen("sequence.out","w", stdout);
scanf("%d%d", &n, &m);
c[0][0] = 1;
for (int i = 1; i <= n + 30; i++)
{
c[i][0] = 1;
for (int j = 1; j <= 20; j++)
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mo;
}
while (m--)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
for (int i = 0; i <= k; i++)
{
f[l][i] = (f[l][i] + c[k][k - i]) % mo;
f[r + 1][i] = (f[r + 1][i] - c[r + 1 - l + k][k - i] + mo) % mo;
}
}
for (int i = 1; i <= n; i++)
for (int j = 0; j <= 20; j++)
f[i + 1][j] = ((f[i + 1][j] + f[i][j]) % mo + f[i][j + 1]) % mo;
for (int i = 1; i <= n; i++)
printf("%lld\n", f[i][0]);
}