9.22模拟赛

T1:水题

T2:留坑

T3:

light

题意:给定长度为n(<=100000)的序列,多次(<=100000)询问,在区间[l,r]中,mod p(1<=1e9)等于v的数有多少个?

其中,序列中每个数的大小<=10000

题解:

p>1e4没有用的。

直接对p处理不好处理。

考虑分块

这里是对p分块,而不是对序列分块(不熟悉,没想到对p分块)!!

sqrt(10000)=100
p<=100时,v<=100

那么,我们可以把询问离线,利用前缀和拆分成两个点,记录前缀和再相减。

然后,我们枚举p,i从1到n扫过去。num[j]记录当前mod p = j的数多少个。

到一个询问点,如果询问的p是当前枚举的p的话,把num[q[i].v]加到ans[q[i].id]里。

复杂度O(n*100)

p>100时,发现,10000/p<100

也就是说,如果x=p*k+v,那么,这个k最多是100

对于这种情况,可以再询问离线拆分从前往后扫描。

num[j]记录,数值为j的数出现多少个。

到达一个询问点的时候,枚举k,统计所有的p*k+v的数个数总和。

复杂度O(n*100)

代码:

注意指针L的边界移动条件等等。

#include<bits/stdc++.h>
using namespace std;
const int N=100000+5;
const int M=10000+5;
int n,m;
int a[N];
int mx;
struct que{
    int pos,v;
    int ad;
    int id;
    int mod;
    void init(int a,int b,int c,int d,int e){
        pos=a,v=b,ad=c,id=d,mod=e;
    }
    bool friend operator <(que a,que b){
        return a.pos<b.pos;
    }
}q1[2*N],q2[2*N];
int cnt1,cnt2;
int num[M];
int ans[N];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),mx=max(mx,a[i]);
    int l,r,p,v;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&l,&r,&p,&v);
        if(p<=100){
            if(l-1)q1[++cnt1].init(l-1,v,-1,i,p);
            q1[++cnt1].init(r,v,1,i,p);
        }
        else{
            if(l-1)q2[++cnt2].init(l-1,v,-1,i,p);
            q2[++cnt2].init(r,v,1,i,p);
        }
    }
    sort(q1+1,q1+cnt1+1);
    sort(q2+1,q2+cnt2+1);
    for(int i=1;i<=100;i++){
        memset(num,0,sizeof num);
        int L=0;
        for(int j=1;j<=cnt1;j++){
            while(L<q1[j].pos){
                L++;
                num[a[L]%i]++;
            }
            if(q1[j].mod==i){
                ans[q1[j].id]+=q1[j].ad*num[q1[j].v];
            }            
        }
    }
    memset(num,0,sizeof num);
    int L=0;
    for(int j=1;j<=cnt2;j++){
        while(L<q2[j].pos){
            //cout<<L<<" : "<<a[L]<<endl;
            L++;
            num[a[L]]++;
        }
        //cout<<" L "<<L<<endl;
        for(int l=0;q2[j].mod*l+q2[j].v<=mx;l++){
            //cout<<q2[j].pos<<" "<<q2[j].id<<" "<<q2[j].ad<<" "<<q2[j].mod<<" "<<q2[j].v<<endl;
            //cout<<" "<<q2[j].mod*l+q2[j].v<<" "<<num[q2[j].mod*l+q2[j].v]<<endl;
            ans[q2[j].id]+=q2[j].ad*num[q2[j].mod*l+q2[j].v];
        }
    }
    for(int i=1;i<=m;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

当然,我们也可以牺牲一个logn在线处理

仍然要对p分块

i207M的做法:

1.p<=100,对序列也分块。f[i][j][k]表示,前i块,mod p=k的数有多少个。

然后块内查询,块外暴力即可。

2.p>100,用一个vector<int>mem[10000]记录,数值为x的数出现的位置。

从前到后扫描,自然排好了序。

然后对于询问,x=p*k+v,

同样利用k<=100,枚举k,然后在mem[p*k+v]内进行二分。找到[l,r]内包含的数。

即可统计。

复杂度O(M*100*logn)(这个logn其实很小,因为每个vector的size总和是n)

也可以通过。

总结:

这个题是这场比赛失败的差距所在。

对分块的运用不够灵活,对p分块是经典的操作,但是竟然不熟悉。。。。

p<=sqrt(n),余数<=sqrt(n)

p>sqrt(n),x=p*k+v的k也<=sqrt(n)

也是一种平均一下复杂度,达到msqrt(n)的均衡。

猜你喜欢

转载自www.cnblogs.com/Miracevin/p/9691549.html