题目大意
数轴上有 n n n 个点,一个机器人从任意位置出发,每个时刻任意向左或者向右,到达一个位置就加上这个位置的分数,求最后走 k k k 步所有可能的分数之和。
并且有 q q q 次询问,每次修改一个点权。
Solution
显然不能直接做。所以考虑拆开算贡献。
对于一个点,我们考虑会在路径中出现多少次。考虑用 d p dp dp 算。
设 c n t i , j cnt_{i,j} cnti,j 为第 i i i 步的时候走到 j j j 号点的路径数,那么有:
c n t i , j = c n t i − 1 , j − 1 + c n t i − 1 , j + 1 cnt_{i,j}=cnt_{i-1,j-1}+cnt_{i-1,j+1} cnti,j=cnti−1,j−1+cnti−1,j+1
但是我们不知道到达第 i i i 号点的时候是路径的第几部,所以考虑再设一个 c n t i , j ′ cnt'_{i,j} cnti,j′ 表示从 j j j 号节点出发,走 i i i 步的路径总数,结果发现递推式和 c n t cnt cnt 一模一样,那就直接用 c n t cnt cnt 了。这样递推是 O ( n 2 ) O(n^2) O(n2) 的可以接受。
然后考虑用 c n t cnt cnt 来求出一个点在所有路径中出现的次数。
枚举点 i i i 在路径中是第 j j j 个点,那么有( m u l mul mul 就表示出现次数):
m u l = ∑ j = 0 k c n t j , i × c n t k − j , i mul=\sum_{j=0}^kcnt_{j,i}\times cnt_{k-j,i} mul=j=0∑kcntj,i×cntk−j,i
于是我们成功求出每个点出现的次数,然后乘上点的权值就是最后对答案的贡献了。
至于修改,用树状数组或者线段树维护一下就可以了。
复杂度是 O ( n 2 + q log n ) O(n^2+q\log n) O(n2+qlogn),可以过。
Code
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
using namespace std;
const int MAXN=5010;
const ll MOD=1e9+7;
ll cnt[MAXN][MAXN],a[MAXN],mul[MAXN];
ll tr[MAXN];
int n;
int lbt(int x){return x&(-x);}
void upd(int x,ll v){
for(int i=x;i<=n;i+=lbt(i))
tr[i]=(tr[i]+v)%MOD;
}
ll ask(int x){
ll ret=0;
for(int i=x;i;i-=lbt(i))
ret=(ret+tr[i])%MOD;
return ret;
}
void solve(){
int k,Q;
scanf("%d%d%d",&n,&k,&Q);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),cnt[0][i]=1;
for(int i=1;i<=k;i++){
for(int j=1;j<=n;j++)
cnt[i][j]=(cnt[i-1][j-1]+cnt[i-1][j+1])%MOD;
}
for(int i=1;i<=n;i++)
for(int j=0;j<=k;j++)
mul[i]=(mul[i]+cnt[j][i]*cnt[k-j][i]%MOD)%MOD;
for(int i=1;i<=n;i++)
upd(i,mul[i]*a[i]%MOD);
int p;ll x;
while(Q--){
scanf("%d%lld",&p,&x);
upd(p,(MOD-a[p]*mul[p]%MOD)%MOD);
a[p]=x;
upd(p,a[p]*mul[p]%MOD);
printf("%lld\n",ask(n)%MOD);
}
}
int main()
{
// int T;
// for(scanf("%d",&T);T--;)
solve();
return 0;
}