题意简介
根据给定的数组 (B),现要求你构造出数组 (A),使数组 (A) 的每个位置 i,都满足下面的条件:
(A_i=B_i) 或 (sum^i_{j=1}A_j=B_i)
求问数组 A 的构造方案有多少种?
思路分析
首先,从通过人数上看,这题比 E 题容易。
显然,这是一道计数的题目。
从题意我们可以看出,对于每个 i 位置上的数,只有下列两种可能:
-
(A_i=B_i)
-
(A_i=B_i-sum) (sum 为 (sum^{i-1}_{j=1}A_j))
假如每个位置都有上述两条世界线可以走的话,最终构造出的数组会有 (2^n) 种。
但事情并没有这么简单,我们发现,存在某些情况下,上述两个条件恰好等价,即 (sum=0)。此时,这一状态便没有两条世界线了。
于是,我们想到,维护到每个位置时各个 sum 对应的世界线的数量,然后在统计 i 的影响时,加入把上面的特殊情况取出来特殊考虑就行了。
不难看出,i 的影响如下:
-
将 sum 变为 (B_i)
-
将 sum 加上 (B_i)
对于第二种情况,可以用全局变量方便地维护。
利用 hash 表维护 sum 对应的世界线数量即可。
代码库
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,a,b) for(int i=a;i>=b;i--)
const ll N=2e5+5,mod=1e9+7,M=300007,STEP=233;
int T,n; ll B[N],delta;
ll hsh[M],cnt[M];
inline int find(ll x){
int p=(x-x/M*M+M)%M;
while(cnt[p]!=0&&hsh[p]!=x) p=(p+STEP)%M;
return p;
}
inline void add(ll x,ll c){
int p=find(x);
hsh[p]=x; cnt[p]=(cnt[p]+c)%mod;
}
inline ll count(ll x){
int p=find(x);
return cnt[p];
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
rep(i,1,n) scanf("%lld",B+i);
memset(cnt,0,sizeof(cnt));
memset(hsh,0,sizeof(hsh));
delta=0; add(0,1); // sum 一开始只有 0 一种
ll ans=1;
rep(i,1,n){
int tmp=(ans-count(-delta)+mod)%mod; // tmp 表示 i-1 的所有方案中 sum 不为 0 的情形 对应两种世界线都可以走的情况
add(-delta,tmp); // 因为之后 bi 会加到 delta 上 所以此处对应 sum 直接变成 bi 的情况。
ans=(ans+tmp)%mod;
delta+=B[i]; // 本来的情况直接加上 bi 的情形
}
printf("%lld
",ans);
}
return 0;
}