【控制dp】Count Subsequences【Codechef】
中文题面
https://s3.amazonaws.com/codechef_shared/download/translated/CK101TST/mandarin/CSUBSQ.pdf
该问题乍一看很容易想到dp。
dp的话,就是按模数分类计数;即f[i][x]表示扫描到i时子序列和的模为x的方案数,转移就不说了。
但是题目限制端点$i_{l}-i_{1} \geq w$,这时候要对dp进行控制,可以用分治算法
考虑区间[l,r],区间中点为mid,那么可以枚举区间末端点$i_{l}$,并使其大于mid,再在mid左端选一些点,从而达到控制端点的目的。
然后分治即可。该题思路大致如此,具体看代码。
分治加上dp的复杂度是On*k*logn
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=1e5+3,K=50+3; typedef long long ll; const int mod=1e9+7; int a[N]; int k,w; ll ans; ll f[N][K],g[N][K]; void solve(int l,int r) { if(r-l<w) return; if(l==r) { if(a[l]==0) ans=(ans+1)%mod; return; } int mid= l+r>>1; memset(f[mid+1],0,sizeof f[mid+1]); memset(g[mid],0,sizeof g[mid]); f[mid+1][0]= g[mid][0]= 1; for(int i=mid;i>=l;i--) for(int x=0;x<k;x++) f[i][x]= (f[i+1][(x-a[i]+k)%k]+f[i+1][x])%mod; for(int i=mid+1;i<=r;i++) for(int x=0;x<k;x++) g[i][x]= (g[i-1][(x-a[i]+k)%k]+g[i-1][x])%mod; ll t1,t2; for(int i=r;i>=mid+1;i--) { if(i-l<w) break; for(int x=0;x<k;x++) { int y=(k-x)%k; t1=g[i][x]-g[i-1][x]; if(i-w+1>mid) t2=f[l][y]- f[mid+1][y]; else t2=f[l][y]-f[i-w+1][y]; t1=(t1+mod)%mod; t2=(t2+mod)%mod; ans=(ans+t1*t2%mod)%mod; } } solve(l,mid); solve(mid+1,r); } int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d%d%d",&n,&k,&w); for(int i=1;i<=n;i++) scanf("%d",&a[i]); ans=0; solve(1,n); printf("%lld\n",ans); } return 0; }