【NOIP2016提高A组模拟9.21】整除

题目

  1. 【NOIP2016提高A组模拟9.21】整除 (Standard IO)
    Time Limits: 2000 ms Memory Limits: 262144 KB Detailed Limits

Description

麦克雷有一个1~n的排列,他想知道对于一些区间,有多少对区间内的数(x,y),满足x能被y整除。

Input

第一行包含2个正整数n,m。表示有n个数,m个询问。
接下来一行包含n个正整数,表示麦克雷有的数列。
接下来m行每行包含2个正整数l,r。表示询问区间[l,r]。

Output

共 m 行,每行一个整数,表示满足条件的对数。

Sample Input

10 9
1 2 3 4 5 6 7 8 9 10
1 10
2 9
3 8
4 7
5 6
2 2
9 10
5 10
4 10

Sample Output

27
14
8
4
2
1
2
7
9

Data Constraint
30%:1<=n,m<=100
100%:1<=n,m<=2*10^5,1<=pi<=n

正解

我不会告诉你我懒得打tj的
令 X = [1, R] 的合法对数减去 [1, L - 1] 的合法对数,
Y = 一个数属于 [1, L - 1] 另一个数属于 [L, R] 的合法对数,
那么答案就是 X - Y。
我们先将询问按右端点升序排序。
并按原序列顺序从 1 往 n 做,每做到一个位置,便在它左边的数中与它有倍数关
系的数的位置加一。
并统计已经加的次数。
那么每当我们遇到一个右端点与当前做的位置相同时,就可以直接将当前总共加
的次数减去起点到左端点的区间的和就行了。
单点修改和区间求和可以用树状数组。

贴代码

var
    tree:array[0..700005]of longint;
    a,b:array[0..200005]of longint;
    q:array[0..200005,1..3]of longint;
    ans:array[0..200005]of longint;
    i,j,k,l,m,n,x,tot,cc:longint;
procedure qsort(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 qsort(i,r);
    if l<j then qsort(l,j);
end;
procedure change(x,l,r,v:longint);
var
    mid:longint;
begin
    if l=r then inc(tree[x]) else
    begin
        mid:=(l+r) div 2;
        if v<=mid then change(x*2,l,mid,v) else change(x*2+1,mid+1,r,v);
        tree[x]:=tree[x]+1;
    end;
end;
procedure find(x,l,r,s,t:longint);
var
    mid:longint;
begin
    if t<s then exit;
    if (l=s) and (r=t) then cc:=cc+tree[x] else
    begin
        mid:=(l+r) div 2;
        if t<=mid then find(x*2,l,mid,s,t) else
        if s>mid then find(x*2+1,mid+1,r,s,t) else
        begin
            find(x*2,l,mid,s,mid);
            find(x*2+1,mid+1,r,mid+1,t);
        end;
    end;
end;
begin
   // assign(input,'t3.in'); reset(input);
   // assign(output,'t3.out'); rewrite(output);
    readln(n,m);
    for i:=1 to n do
    begin
        read(a[i]);
        b[a[i]]:=i;
    end;
    readln;
    for i:=1 to m do
    begin
        readln(q[i,1],q[i,2]);
        q[i,3]:=i;
    end;
    qsort(1,m);
    x:=0;
    q[m+1,2]:=n*2;
    q[0,2]:=0;
    for i:=1 to n do
    begin
        while q[x,2]<i do inc(x);
        if x>m then break;
        for j:=1 to trunc(sqrt(a[i])) do
        if a[i] mod j=0 then
        begin
            if b[j]<i then
            begin
                change(1,1,n,b[j]);
                inc(tot);
            end;
            if j*j=a[i] then continue;
            if b[a[i] div j]<i then
            begin
                change(1,1,n,b[a[i] div j]);
                inc(tot);
            end;
        end;
        j:=a[i];
        while j<=n do
        begin
            if b[j]<=i then
            begin
                inc(tot);
                change(1,1,n,b[j]);
            end;
            j:=j+a[i];
        end;
        while q[x,2]=i do
        begin
            cc:=0;
            find(1,1,n,1,q[x,1]-1);
            ans[q[x,3]]:=tot-cc;
            inc(x);
        end;
    end;
    for i:=1 to m do writeln(ans[i]);
   // close(input); close(output);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值