题目描述:
紫妈有n 个隙间排成一列,每个隙间都有一个权值 val 。
她可以选出某些隙间来召唤式神:一组隙间能成功召唤式神当且仅当他们的权值和为m
的倍数。(可以是0 倍)
现在紫妈试图召唤Q次式神,每次给出一个 l和r ,她试图在第 l到r 个隙间中召唤式神,
她会选择其中一些隙间(不一定需要连续的一些)召唤式神。她想知道,有多少种方案可以
成功召唤式神。
输入:
第一行两个数,n 和m。
第二行n 个数,表示i val 。
第三行一个数,表示Q。
下面Q行,每行两个数,表示i i l和r 。
输出:
Q行,每行一个数,表示方案数,方案数mod (109 7 )输出。
数据范围:
n,q≤2*105,m≤20
算法标签:(类整体二分),分治
思路:
把问题分治,每次只管理跨过区间的情况,代码看起来很暴力,但是仔细一算时间发现的确优秀。
jzy的整体二分||分治初体验
以下代码:
#include<bits/stdc++.h> #define il inline #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=2e5+5,p=1e9+7; struct npode{int l,r,ans;}t[N]; int n,m,q,res[N],f[N][22],g[N][22],a[N],tmp[N],val[N],b[N]; il int read(){int x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return f*x;} il int mu(int x){if(x>=p)return x-p;return x;} il void solve(int l,int r,int L,int R){ int mid=(l+r)>>1; if(l==r){ for(int i=L;i<=R;i++){ t[a[i]].ans=(val[l]==0?2:1); } return; } for(int j=1;j<m;j++)f[mid+1][j]=0; f[mid+1][0]=1; for(int i=mid;i>=l;i--){ for(int j=0;j<m;j++){ int now=(j-val[i]+m)%m; f[i][j]=mu(f[i+1][now]+f[i+1][j]); } } for(int j=1;j<m;j++)g[mid][j]=0; g[mid][0]=1; for(int i=mid+1;i<=r;i++){ for(int j=0;j<m;j++){ int now=(j-val[i]+m)%m; g[i][j]=mu(g[i-1][now]+g[i-1][j]); } } int tot1=L-1,tot2=R+1; for(int i=L;i<=R;i++){ if(t[a[i]].r<=mid)b[++tot1]=a[i]; else if(t[a[i]].l>mid)b[--tot2]=a[i]; else{ t[a[i]].ans=mu(t[a[i]].ans+(LL)f[t[a[i]].l][0]*(LL)g[t[a[i]].r][0]%p); for(int j=1;j<m;j++){ t[a[i]].ans=mu(t[a[i]].ans+(LL)f[t[a[i]].l][j]*(LL)g[t[a[i]].r][m-j]%p); } } } for(int i=L;i<=tot1;i++)a[i]=b[i]; for(int i=tot2;i<=R;i++)a[i]=b[i]; if(tot1>=L)solve(l,mid,L,tot1); if(tot2<=R)solve(mid+1,r,tot2,R); } int main() { n=read();m=read();for(int i=1;i<=n;i++)val[i]=read()%m; q=read();for(int i=1;i<=q;i++)t[i].l=read(),t[i].r=read(); for(int i=1;i<=q;i++)a[i]=i;solve(1,n,1,q); for(int i=1;i<=q;i++)printf("%d ",t[i].ans); return 0; }