【BZOJ】2506calc-DP

传送门:bzoj2506


题解

非常妙的dp!
对于100以内,直接预处理。
100以上,直接找(1e4的范围使次数严格不大于100)
但这样还不够,
我们把询问离线下来,记录每个询问的左右端点,用2*n的结构体分别记录,然后sort一遍,直接从左到右扫,扫到一个点就记录一下,回答的时候拿终点和起点-1做前缀和的差。
orzzzzhzwer


代码

//made by cosi
#include<bits/stdc++.h>
#define gc getchar()
using namespace std;
const int N=1e5+10;

int ans[2][N],d[N],f[102][102],n,m,cnt,a[N];

struct P{
    int x,p,k,f,num;
}t[N<<1];
bool cmp(const P& a,const P& b){return a.x<b.x;}

inline int rd()
{
    char ch=gc;int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=gc;}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=gc;}
    return x*f;
}

inline void add(int x)
{
    for(int i=1;i<=100;i++) f[i][a[x]%i]++;
    d[a[x]]++;
}

int main(){
    n=rd();m=rd();
    for(int i=1;i<=n;i++) a[i]=rd();
    for(int i=1;i<=m;i++){
       int l=rd(),r=rd(),p=rd(),k=rd();
       t[++cnt].x=l-1;t[cnt].p=p;t[cnt].k=k;t[cnt].num=i;
       t[++cnt].x=r;t[cnt].p=p;t[cnt].k=k;t[cnt].num=i;t[cnt].f=1;
    }
    sort(t+1,t+cnt+1,cmp);
    int now=0;
    for(int i=1;i<=cnt;i++){
        while(now<t[i].x){now++;add(now);}
        int p=t[i].p,k=t[i].k;
        if(p<=100) ans[t[i].f][t[i].num]=f[p][k];
        else{
            for(int j=k;j<=10000;j+=p) ans[t[i].f][t[i].num]+=d[j];
        }
    }
    for(int i=1;i<=m;i++) printf("%d\n",ans[1][i]-ans[0][i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/80294380