式神守护

6 篇文章 0 订阅

式神守护

n,q 2e5  m 20


solution

好题

?pts dp。令f[i][j]表示前i个数组成j的方案数。f[i+1][j+a[k]]=f[i][j+a[k]]+f[i][j].O(q*n*m*m);

40pts 线段树维护答案 效率O(nlogn*m^2)

100pts 考虑分治

假设我正在处理(l,r)

把跨过mid的询问提出来,剩下的分治处理。

把l~mid,mid~r分别做dp,然后对于每个询问再O(m)把两端dp值合起来

效率O(nlogn+qm)

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 200005
#define ll long long
#define mod 1000000007
using namespace std;
int n,m,a[maxn],Q,ans[maxn];
ll f[maxn][22],g[maxn][22];
struct node{
    int l,r,id;
}q[maxn],s[maxn];
void solve(int l,int r,int ql,int qr){
    if(ql>qr)return;
    if(l==r){
        for(int i=ql;i<=qr;i++){
            ans[q[i].id]=1;
        }return;
    }
    int mid=l+r>>1;
    int t1=0,t2,t3;
     
    for(int i=ql;i<=qr;i++)if(q[i].r<=mid)s[++t1]=q[i];
    t2=t1;
    for(int i=ql;i<=qr;i++)if(q[i].l<=mid&&q[i].r>mid)s[++t2]=q[i];
    t3=t2;
    for(int i=ql;i<=qr;i++)if(q[i].l>mid)s[++t3]=q[i];
    for(int i=1,j=ql;i<=t3;i++,j++)q[j]=s[i];
    solve(l,mid,ql,ql+t1-1);solve(mid+1,r,ql+t2,qr);
    for(int i=l;i<=mid+1;i++)
        for(int j=0;j<m;j++)f[i][j]=0;
    for(int i=mid;i<=r;i++)
        for(int j=0;j<m;j++)g[i][j]=0;
    f[mid+1][0]=1;g[mid][0]=1;
     
    for(int i=mid+1;i>=l;i--){
        for(int j=0;j<m;j++){
            f[i-1][(j+a[i-1])%m]=(f[i-1][(j+a[i-1])%m]+f[i][j])%mod;
        }
        for(int j=0;j<m;j++)f[i-1][j]+=f[i][j],f[i-1][j]%=mod;
    }
    for(int i=mid;i<=r;i++){
        for(int j=0;j<m;j++){
            g[i+1][(j+a[i+1])%m]=(g[i+1][(j+a[i+1])%m]+g[i][j])%mod;
        }
        for(int j=0;j<m;j++)g[i+1][j]+=g[i][j],g[i+1][j]%=mod;
    }
    for(int i=ql+t1;i<ql+t2;i++){
        int L=q[i].l,R=q[i].r;ll Ans=0;
        for(int j=0;j<m;j++){
            int t=m-j;if(t>=m)t-=m;
            Ans=Ans+f[L][j]*g[R][t]%mod;Ans%=mod;
        }
        ans[q[i].id]=Ans;
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]%=mod;
    }
    cin>>Q;
    for(int i=1;i<=Q;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    solve(1,n,1,Q);
    for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值