F. Copy or Prefix Sum
Venice technique简要就是懒标记思想。
由于前缀和数组和原数组一一对应,这里我们选择求aia_iai的前缀和数组的方案数(下面aia_iai表示原题数组的前缀和)
不难得知原题目的两个条件即
- bi=ai−ai−1→ai=bi+ai−1b_i=a_i-a_{i-1} \to a_i=b_i+a_{i-1}bi=ai−ai−1→ai=bi+ai−1
- bi=ai→ai=bib_i=a_i \to a_i=b_ibi=ai→ai=bi
状态表示:fi,jf_{i,j}fi,j考虑前iii个数,所求数组第iii个位置值是jjj的方案数。
答案即是:∑j=−∞+∞fn,j\sum_{j=-\infty}^{+\infty}f_{n,j}∑j=−∞+∞fn,j
状态转移:
- fi,j=fi−1,j−bif_{i,j}=f_{i-1,j-b_i}fi,j=fi−1,j−bi
- fi,bi=∑j=−∞+∞fi−1,j−(fi−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)
ai=bi+ai−1a_i=b_i+a_{i-1}ai=bi+ai−1和ai=bia_i=b_iai=bi当ai−1=0a_{i-1}=0ai−1=0时是同一种情况,因此需要把重复计算的去掉。
按照上述转移方式肯定不可信,不难发现第一维可以用滚动数组优化掉,注意第一个转移式子,相当于将整个数组平移bib_ibi,这里采用的懒标记的思想做一个下标映射。
对于第一种转移,维护一个add,fi,j=fi−1,j−bi=fi−1,j+addf_{i,j}=f_{i-1,j-b_i}=f_{i-1,j+add}fi,j=fi−1,j−bi=fi−1,j+add,如果每次让add减去bib_ibi就完成了对数组的平移操作也就是第一种转移。
而下面一种转移,只需要记住原来bib_ibi的位置是bi+addb_i+addbi+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;
}