【控制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
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/mgnfcnt/p/10440781.html