[The Preliminary Contest for ICPC Asia Xuzhou 2019 - 徐州网络赛I] query

query

Given a permutation pp of length nn, you are asked to answer mm queries, each query can be represented as a pair (l ,r )(l,r), you need to find the number of pair(i ,j)(i,j) such that l \le i < j \le rl≤i<j≤r and \min(p_i,p_j) = \gcd(p_i,p_j )min(pi​,pj​)=gcd(pi​,pj​).

Input

There is two integers n(1 \le n \le 10^5)n(1≤n≤105), m(1 \le m \le 10^5)m(1≤m≤105) in the first line, denoting the length of pp and the number of queries.

In the second line, there is a permutation of length nn, denoting the given permutation pp. It is guaranteed that ppis a permutation of length nn.

For the next mm lines, each line contains two integer l_ili​ and r_i(1 \le l_i \le r_i \le n)ri​(1≤li​≤ri​≤n), denoting each query.

Output

For each query, print a single line containing only one integer which denotes the number of pair(i,j)(i,j).

样例输入

3 2
1 2 3
1 3
2 3

样例输出

2
0

 题意

给定一个长度为 n 的连续的序列 p,询问 m 次,在区间 [l, r] 中,有多少组 i、j 使得 min(p[i], p[j]) = gcd(p[i], p[j])。

思路

min(p[i], p[j]) = gcd(p[i], p[j]) 实际上就表示 p[i]、p[j] 成倍数关系。由于询问次数过多,我们可以离线处理好序列中的倍数关系。

首先,读入数据时,我们将每个值对应的位置记录下来。然后,对于 [1, n] 的每个值 p[i],找出序列中与 p[i] 成倍数关系的值 p[j](p[j] = k*p[i], k = 2, 3, 4...),得到它的位置 j,并与 i 构成一个区间 [i, j],可以称之为贡献区间。若 [i, j] 在查询的范围 [l, r] 之内,那么就能为答案贡献 +1。当我们找到所有这样的区间时,将这些贡献区间与询问区间混合,并进行排序,排序的规则是按照区间的左端点从大到小排列,当贡献区间与询问区间的左端点相同时,则贡献区间在前。然后依次处理每个区间,当遇到贡献区间时,说明贡献区间的右端点(含)前的区间都存在一个成倍数点对(因为是按照左端点从大到小处理的,下一个处理的区间的左端点一定小于等于当前区间左端点,所以一定将当前贡献区间包含,当前贡献区间也就能对后续的询问区间的答案产生贡献),我们用树状数组更新这个值;当遇到询问区间时,由于必须完全包含某个贡献区间,该贡献区间才能对答案产生贡献,所以,我们利用树状数组,得到询问区间的右端点前的贡献区间的个数,即为该询问区间的答案。最后,由于询问是离线处理的,所以要根据询问的顺序依次输出答案。

代码

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5+5;
struct query
{
    int l, r, k, f;
    query() {}
    query(int l, int r, int k, int f): l(l), r(r), k(k), f(f) {}
    bool operator < (const query& Q) const
    {
        if(l != Q.l)
            return l > Q.l;
        return f > Q.f; //让离线先处理
    }
}q[maxn<<6];
int n, m;
int pos[maxn], cnt;
int a[maxn], ft[maxn];
int ans[maxn];

int lowbit(int x)
{
    return x & -x;
}

void update(int x, int val)
{
    while(x <= n)
    {
        ft[x] += val;
        x += lowbit(x);
    }
}

int getSum(int x)
{
    int ret = 0;
    while(x > 0)
    {
        ret += ft[x];
        x -= lowbit(x);
    }
    return ret;
}

void read()
{
    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
    {
        cin >> a[i];
        pos[a[i]] = i;
    }
    int l, r;
    for(int i = 0; i < m; ++i)
    {
        cin >> l >> r;
        q[cnt++] = query(l, r, i, 0);
    }
}

void solve()
{
    for(int i = 1; i <= n; ++i)
        for(int j = 2*a[i]; j <= n; j += a[i])
            q[cnt++] = query(min(i, pos[j]), max(i, pos[j]), 0, 1);
    sort(q, q+cnt);
    for(int i = 0; i < cnt; ++i)
    {
        if(q[i].f)
            update(q[i].r, 1);
        else
            ans[q[i].k] = getSum(q[i].r);
    }
    for(int i = 0; i < m; ++i)
        cout << ans[i] << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    read();
    solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/HNUCSEE_LJK/article/details/100638270