F. Copy or Prefix Sum
Venice technique简要就是懒标记思想。
由于前缀和数组和原数组一一对应,这里我们选择求
a
i
a_i
ai的前缀和数组的方案数(下面
a
i
a_i
ai表示原题数组的前缀和)
不难得知原题目的两个条件即
- b i = a i − a i − 1 → a i = b i + a i − 1 b_i=a_i-a_{i-1} \to a_i=b_i+a_{i-1} bi=ai−ai−1→ai=bi+ai−1
- b i = a i → a i = b i b_i=a_i \to a_i=b_i bi=ai→ai=bi
状态表示:
f
i
,
j
f_{i,j}
fi,j考虑前
i
i
i个数,所求数组第
i
i
i个位置值是
j
j
j的方案数。
答案即是:
∑
j
=
−
∞
+
∞
f
n
,
j
\sum_{j=-\infty}^{+\infty}f_{n,j}
∑j=−∞+∞fn,j
状态转移:
- f i , j = f i − 1 , j − b i f_{i,j}=f_{i-1,j-b_i} fi,j=fi−1,j−bi
- f i , b i = ∑ j = − ∞ + ∞ f i − 1 , j − ( f i − 1 , 0 ) f_{i,b_i}=\sum_{j=-\infty}^{+\infty}f_{i-1,j}-(f_{i-1,0}) fi,bi=∑j=−∞+∞fi−1,j−(fi−1,0)
a i = b i + a i − 1 a_i=b_i+a_{i-1} ai=bi+ai−1和 a i = b i a_i=b_i ai=bi当 a i − 1 = 0 a_{i-1}=0 ai−1=0时是同一种情况,因此需要把重复计算的去掉。
按照上述转移方式肯定不可信,不难发现第一维可以用滚动数组优化掉,注意第一个转移式子,相当于将整个数组平移 b i b_i bi,这里采用的懒标记的思想做一个下标映射。
对于第一种转移,维护一个add, f i , j = f i − 1 , j − b i = f i − 1 , j + a d d f_{i,j}=f_{i-1,j-b_i}=f_{i-1,j+add} fi,j=fi−1,j−bi=fi−1,j+add,如果每次让add减去 b i b_i bi就完成了对数组的平移操作也就是第一种转移。
而下面一种转移,只需要记住原来 b i b_i bi的位置是 b i + a d d b_i+add bi+add即可转移
#define IO ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
#pragma GCC optimize(2)
#include<map>
#include<iostream>
#include<algorithm>
using namespace std;
using ll=long long;
constexpr int N=100010;
constexpr ll mod=1e9+7;
int n;
map<ll,ll> dp;
int main()
{
IO;
int T=1;
cin>>T;
while(T--)
{
cin>>n;
dp.clear();
dp[0]=1;
ll sum=1,add=0;
for(int i=1;i<=n;i++)
{
ll b;cin>>b;
ll pre=sum-dp[0+add]; pre=(pre%mod+mod)%mod;
add-=b;
sum+=pre; sum%=mod;
dp[b+add]+=pre; dp[b+add]%=mod;
}
cout<<sum<<'\n';
}
return 0;
}