式神守护
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;
}