【AFOI-19】区间与除法

前言

利益相关:经常骗吃骗喝,也经常被出题人欺负QAQ。

话说出题组的科技树点得有点歪(其实不是一般的歪)...要是认真觉得他们很综合性的话你就输啦!

题解

出题人上课摸鱼出得这题,给我口胡这题问我怎么做,然后我犯蠢了......我瞎扯了一通(自己都心虚的那种)准备水过去...

但是在讨论的过程中,出题人说要用tire树,然后我就突然发现....\(d\)固定的情况下,根本不用去考虑怎么取最优方案来覆盖尽可能多的数,实际上,如果\(b_i\)\(a\)的原数,\(b_j\)也是\(a\)的原数的话,那么\(min(b_i,b_j)\)就是这三个数的公共原数....

所以......直接枚举\(a_i\),然后依次除\(d\),若除的过程中\(a_i==b_j\),则取最小的\(b_j\),怎么快速找\(a_i==b_j\)呢?用出题人的方案或者数据结构的话太复杂了并且带个\(log\),把\(b_i\)排个序,\(a_i\)在除的过程中递减,\(b_j\)也随之递减,这样来找就行了,这一部分复杂度就是\(O(nlogn)\),取大一点的话其实是\(O(nm)\),但是\(m\)小于等于\(60\)

剩下的,对于每一个\(a_i\)我们都找到了其唯一对应最小的\(b_j\)\(j\)小于等于\(60\),考虑状压,一个区间里面的状压或起来就是最优方案了,最后随便是\(st\)表还是线段树维护一下区间或值都可以。

\(1.5s\)的话算良心了(嘿嘿)。


#include <bits/stdc++.h>
using namespace std;

void readll(long long &an){
    char ch=getchar();an=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')an=(an<<3)+(an<<1)+ch-'0',ch=getchar();
}
void read(int &an){
    char ch=getchar();an=0;
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')an=(an<<3)+(an<<1)+ch-'0',ch=getchar();
}
int n,m,d,q;
long long b[65];
long long a[500006];
long long c[500005];
long long t[2000005];
void build(int l,int r,int id){
    if(l==r){
        t[id]=c[l];
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,id*2);
    build(mid+1,r,id*2+1);
    t[id]=t[id*2]|t[id*2+1];
}

long long query(int l,int r,int z,int y,int id){
    if(l==z&&r==y)return t[id];
    int mid=(l+r)/2;
    if(mid>=y)return query(l,mid,z,y,id*2);
    else if(mid<z)return query(mid+1,r,z,y,id*2+1);
    else return query(l,mid,z,mid,id*2)|query(mid+1,r,mid+1,y,id*2+1);
}
int get(long long x){
    int ans=0;
    while(x){
        ans++;
        x-=(x&(-x));
    }
    return ans;
}

int main(){
    read(n),read(m),read(d),read(q);
    for(int i=1;i<=n;i++){
        readll(a[i]);
    }
    for(int i=1;i<=m;i++){
        readll(b[i]);
    }
    sort(b+1,b+1+m);
    int rr=m;
    for(int i=1;i<=n;i++)
    {
        rr=m;
        while(a[i]){
            while(rr&&b[rr]>a[i])rr--;
            if(b[rr]==a[i])c[i]=(1ll<<(rr));
            a[i]/=d;
        }
    }
    build(1,n,1);
    for(int i=1;i<=q;i++){
        int l,r;
        read(l),read(r);
        printf("%d\n",get(query(1,n,l,r,1)));
    }
    return 0;
}
posted @ 2019-11-04 19:25  redegg  阅读(523)  评论(4编辑  收藏  举报