Problem
传送门:CF1485F Copy or Prefix Sum
给出一个长度为 (n) 的序列 (b_1,b_2,dots ,b_n) ,求出有多少种序列(a_1,a_2,dots,a_n) 满足:
(forall i in [1,n],b_i = a_i or b_i = sum_limits{j=1}^i a_j).
Sol
考虑当前我们填到第 (i) 位,所填的数的和是 (S) , 那么,我们可以填:
1.(b_i)
2.(b_i-S)
于是,我们可以记(dp_i) 代表当前填的数之和为 (i) 的方案数,于是,两种操作对应这:
1.(dp_{i+b_i} = dp_i)
2.(dp_{b_i} = sum_limits{j=-inf}^inf{dp_j})
但是要注意,当 (b_i = b_i - S) 也就是 (S = 0) 时,是只能算一遍的,减去 (dp_0) 即可。
然后维护一个 (map) ,代表 (DP) 数组,维护一个 (totadd) ,代表全局移位(因为有操作一),再维护一个目前的 (ans),就搞完了。
Code
#define in read()
int read(){int x = 0,sgn = 1;char ch = getchar(); for(;!isdigit(ch);ch=getchar())if(ch=='-')sgn=-1;for(;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);return x*sgn;}
const int N = 2e5+10;
const int mod = 1e9+7;
int n,b[N];
void solve(){
n = in;for(int i = 1;i <= n;i++) b[i] = in;
map<ll,ll> f; f.clear();
f[0] = 1; ll totadd = 0,ans = 1;
for(int i = 1;i <= n;i++){
ll tmp; tmp = (ans - f[-totadd] + mod)% mod; ans = (ans + tmp) % mod; (f[-totadd] += tmp) %= mod;
totadd += b[i];
}ans = (ans + mod) %mod;
printf("%lld
",ans);
}
int main(){
int test = in;
while(test--) solve();
return 0;
}
彩蛋
1.出题人说 "F is easier than C."确实。
- 于是我打完 C 后开始搞 F
- 搞完了 F,确实是正解,但是已经 0 :53 了,比赛已经结束了 3 分钟
- 其实 F 早就搞完了,有两个神奇的错误调了一会儿,Can you guess ?