codeforces1485 F. Copy or Prefix Sum(dp)

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=aiai1ai=bi+ai1
  • b i = a i → a i = b i b_i=a_i \to a_i=b_i bi=aiai=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=fi1,jbi
  • 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=+fi1,j(fi1,0)

a i = b i + a i − 1 a_i=b_i+a_{i-1} ai=bi+ai1 a i = b i a_i=b_i ai=bi a i − 1 = 0 a_{i-1}=0 ai1=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=fi1,jbi=fi1,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;
}

猜你喜欢

转载自blog.csdn.net/Fighting_Peter/article/details/113802017