【NOIP2016提高A组模拟9.2】同余

Description
这里写图片描述
Input
这里写图片描述
Sample Input

5 2
1 5 2 3 7
1 3 2 1
2 5 3 0
Output
这里写图片描述
Sample Output

2
1
Data Constraint
这里写图片描述

题目大意

有很多询问,每一次询问询问一个区间内有多少个数被p除后余q

比赛时の想法

想了两分钟后想到了60%的前缀和做法,又想了一下,发现好像可以用莫队来做这道题
莫队的做法:先把输入存起来以l为关键字排一次序,然后用莫队的思想分块,每个块再以r为关键字从小到大排一次序,然后用一个桶来维护当前区间每一个数有多少个。最后暴力的计算答案,然而以为我没有打拍,有一个大于号打成小于号了,于是愉悦的爆零

正解

把每一个询问拆分成1->l-1和1->r的形式,然后排一次序,那么就可以用扫描线做这道题了,我们存储两个桶,一个是g[i][j],表示被i除了之后余数是j的数有多少个(i<=100)另外一个是sum[i],表示当前数字i出现了多少次,对于一个询问,如果p小于100,那么直接在g数组中可以直接获得答案,如果p大于100,那么就在sum数组中把所有合法的情况加起来就可以了,这样做的时间复杂度是 10000m2 的(其实我觉得莫队也可以这样做啊)

贴代码

我的代码其实是有点水的,如果要卡是会被卡掉的
话说比我的莫队模板还要短哎!

var
    a,c:array[0..100005]of longint;
    q:array[0..100005,1..5]of longint;
    sum,tot:array[0..100005]of longint;
    i,j,k,l,m,n,x,y,maxx,p,ans:longint;
procedure qsort(l,r:longint);
var
    i,j,mid:longint;
begin
    i:=l;
    j:=r;
    mid:=q[(i+j) div 2,1];
    repeat
        while q[i,1]<mid do inc(i);
        while q[j,1]>mid do dec(j);
        if i<=j then
        begin
            q[0]:=q[i];
            q[i]:=q[j];
            q[j]:=q[0];
            inc(i);
            dec(j);
        end;
    until i>j;
    if i<r then qsort(i,r);
    if l<j then qsort(l,j);
end;
procedure qsort1(l,r:longint);
var
    i,j,mid:longint;
begin
    i:=l;
    j:=r;
    mid:=q[(i+j) div 2,2];
    repeat
        while q[i,2]<mid do inc(i);
        while q[j,2]>mid do dec(j);
        if i<=j then
        begin
            q[0]:=q[i];
            q[i]:=q[j];
            q[j]:=q[0];
            inc(i);
            dec(j);
        end;
    until i>j;
    if i<r then qsort1(i,r);
    if l<j then qsort1(l,j);
end;
procedure work1(x,y:longint);
var
    i:longint;
begin
    for i:=x to y-1 do dec(sum[a[i]]);
end;
procedure work2(x,y:longint);
var
    i:longint;
begin
    for i:=x downto y+1 do dec(sum[a[i]]);
end;
procedure work3(x,y:longint);
var
    i:longint;
begin
    for i:=x+1 to y do inc(sum[a[i]]);
end;
procedure work4(x,y:longint);
var
    i:longint;
begin
    for i:=x-1 downto y do inc(sum[a[i]]);
end;
procedure init;
begin
    readln(n,m);
    for i:=1 to n do
    begin
        read(a[i]);
        if a[i]>p then p:=a[i];
    end;
    readln;
    for i:=1 to m do
    begin
        readln(q[i,1],q[i,2],q[i,3],q[i,4]);
        q[i,5]:=i;
    end;
    qsort(1,m);
    maxx:=trunc(sqrt(q[m,1]));
    c[0]:=1;
    c[1]:=1;
    x:=0;
    for i:=1 to m do
    begin
        if x<q[i,1] then
        begin
            inc(c[0]);
            c[c[0]]:=i;
        end;
        while x<q[i,1] do x:=x+maxx;
    end;
    for i:=1 to c[0]-1 do qsort1(c[i],c[i+1]-1);
    x:=1;
    y:=1;
    sum[a[1]]:=1;
end;
begin
    //assign(input,'3.in'); reset(input);
    init;
    for i:=1 to m do
    begin
        ans:=0;
        if x<q[i,1] then work1(x,q[i,1]) else work4(x,q[i,1]);
        if y>q[i,2] then work2(y,q[i,2]) else work3(y,q[i,2]);
        while q[i,4]<=p do
        begin
            ans:=ans+sum[q[i,4]];
            q[i,4]:=q[i,4]+q[i,3];
        end;
        tot[q[i,5]]:=ans;
        x:=q[i,1];
        y:=q[i,2];
    end;
    for i:=1 to m do writeln(tot[i]);
   // close(input);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值