题目
- 【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.