2018.11.01-dtoj-4020: 式神守护(yukari)——(类)整体二分

题目描述:

紫妈有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\n",t[i].ans);
  return 0;
}
View Code

 

posted @ 2018-11-01 15:39  Jessiejzy  阅读(216)  评论(0编辑  收藏  举报