Subsequence Sum Queries

Problem

有一个长度为\(n\)的数列,一个数\(m\)\(q\)个询问,每次询问区间\([l_i, r_i]\)中有多少子序列的和是\(m\)的倍数。

Solution

分治,将询问分配到各个区间。若一个区间的左区间完全包含某个询问的询问区间,则将该询问分配给该区间的左区间去处理,反之亦然。若某个询问的询问区间跨立于该区间的左右两个区间之间,则在该区间进行处理。

Code

/*
 Author: LargeDumpling
 Email: [email protected]
 Edit History:
    2018-07-24  File created.
*/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=200050;
const int mod=1000000007;
struct jz
{
    int l,r;
    long long ans;
    jz() { l=r=ans=0; }
}Q[MAXN];
int n,m,qn,a[MAXN];
long long f[MAXN][20];
void read1n(int &x)
{
    char ch;
    for(ch=getchar();ch<'0'||'9'<ch;ch=getchar());
    for(x=0;'0'<=ch&&ch<='9';ch=getchar())
        x=(x<<1)+(x<<3)+ch-'0';
    return;
}
void calc(int l,int r,vector<int> &q)
{
    if(l==r)
    {
        int cnt=1;
        if(a[l]%m==0) cnt++;
        for(unsigned i=0;i<q.size();i++)
            Q[q[i]].ans=cnt;
        return;
    }
    int mid=(l+r)>>1;
    vector<int> qn[2];
    /*for(int i=l;i<=r;i++)
    {
        memset(f[i],0,sizeof(f[i]));
        f[i][0]++;
        f[i][a[i]%m]++;
    }
    for(int i=mid-1;l<=i;i--)
    {
        long long tem[20];
        memset(tem,0,sizeof(tem));
        for(int j=0;j<m;j++) // This is not necessary, j is useful only when j==0 or j==a[i]
            for(int k=0;k<m;k++)
                tem[(j+k)%m]=(tem[(j+k)%m]+f[i][j]*f[i+1][k]%mod)%mod;
        memcpy(f[i],tem,sizeof(tem));
    }
    for(int i=mid+2;i<=r;i++)
    {
        long long tem[20];
        memset(tem,0,sizeof(tem));
        for(int j=0;j<m;j++)
            for(int k=0;k<m;k++)
                tem[(j+k)%m]=(tem[(j+k)%m]+f[i][j]*f[i-1][k]%mod)%mod;
        memcpy(f[i],tem,sizeof(tem));
    }*/
    memset(f[mid],0,sizeof(f[mid]));
    f[mid][0]++; f[mid][a[mid]%m]++;
    memset(f[mid+1],0,sizeof(f[mid+1]));
    f[mid+1][0]++; f[mid+1][a[mid+1]%m]++;
    long long tem[20];
    for(int i=mid-1;l<=i;i--)
    {
        memset(tem,0,sizeof(tem));
        for(int j=0;j<m;j++)
        {
            tem[j]=(tem[j]+f[i+1][j])%mod;
            tem[(j+a[i])%m]=(tem[(j+a[i])%m]+f[i+1][j])%mod;
        }
        memcpy(f[i],tem,sizeof(tem));
    }
    for(int i=mid+2;i<=r;i++)
    {
        memset(tem,0,sizeof(tem));
        for(int j=0;j<m;j++)
        {
            tem[j]=(tem[j]+f[i-1][j])%mod;
            tem[(j+a[i])%m]=(tem[(j+a[i])%m]+f[i-1][j])%mod;
        }
        memcpy(f[i],tem,sizeof(tem));
    }
    for(unsigned i=0;i<q.size();i++)
    {
        if(Q[q[i]].r<=mid) qn[0].push_back(q[i]);
        if(mid<Q[q[i]].l) qn[1].push_back(q[i]);
        if(Q[q[i]].l<=mid&&mid<Q[q[i]].r)
        {
            for(int j=0;j<m;j++)
                Q[q[i]].ans=(Q[q[i]].ans+f[Q[q[i]].l][j]*f[Q[q[i]].r][(m-j)%m])%mod;
        }
    }
    if(qn[0].size()) calc(l,mid,qn[0]);
    if(qn[1].size()) calc(mid+1,r,qn[1]);
    return;
}
int main()
{
    read1n(n); read1n(m);
    for(int i=1;i<=n;i++)
        read1n(a[i]);
    read1n(qn);
    vector<int> q;
    for(int i=1;i<=qn;i++)
    {
        read1n(Q[i].l);
        read1n(Q[i].r);
        q.push_back(i);
    }
    calc(1,n,q);
    for(int i=1;i<=qn;i++)
        printf("%lld\n",Q[i].ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LargeDumpling/p/9369144.html