ACM-ICPC 2018 徐州赛区网络预赛 I. query 树状数组

I. query

题目链接:

Problem Description

Given a permutation \(p\) of length \(n\), you are asked to answer \(m\) queries, each query can be represented as a pair \((l ,r )\), you need to find the number of pair \((i ,j)\) such that \(l \le i < j \le r\) and \(\min(p_i,p_j) = \gcd(p_i,p_j )\).

Input

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

In the second line, there is a permutation of length \(n\), denoting the given permutation \(p\). It is guaranteed that \(p\) is a permutation of length \(n\).

For the next \(m\) lines, each line contains two integer \(l_i\) and \(r_i(1 \le l_i \le r_i \le n)\), denoting each query.

Output

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

样例输入

3 2
1 2 3
1 3
2 3

样例输出

2
0

题意

给你一个序列,求很多段子区间\((l ,r )\)满足\(l \le i < j \le r\) and \(\min(p_i,p_j) = \gcd(p_i,p_j )\) 的个数。

题解

1.转化一下就是求一个区间有多少对满足一个是另一个的倍数。

2.我们会发现这个是一个排列,每个数x的倍数个数为\(\frac{n}{x}\),那么所有的倍数个数即为\(\sum_{i=1}^{n}\frac{n}{i})(\le nlog_{2}{n+1})\)

3.我们将所有倍数点对预处理出来,问题就变成了问一个区间有多少倍数点对同时存在。

4.是不是很熟悉啦(不知道也没关系),我来细细讲解一下:

  • 先将区间按右端点从小到大排序,保证右端点单调递增
  • 那么起作用的就是左端点,这是我们碰到一个点就将它左边的所有是它约数以及倍数的位置权值全部+1,这样如果左边这个点在区间里,右端点必然也在区间里因为右端点单调递增。

如果真的理解了的话想想按左端点从大到小也可以做,想想怎么做?

其实这题是cf原题,网络赛时我不会做,然后竟然搜到了原题(还是有极其微小的差异),然后现学啦,哈哈哈。

cf链接:codeforces 301D

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x7f7f7f7f
#define N 100050
template<typename T>void read(T&x)
{
    ll k=0; char c=getchar();
    x=0;
    while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
    if (c==EOF)exit(0);
    while(isdigit(c))x=x*10+c-'0',c=getchar();
    x=k?-x:x;
}
void read_char(char &c)
{while(!isalpha(c=getchar())&&c!=EOF);}
int n,m,a[N],p[N],c[N],ans[N];
vector<int>vec[N];
struct Query
{
    int l,r,id;
    bool operator <(const Query b)const
        {return r<b.r;}
}que[N];
void change(int x){while(x<=n)c[x]++,x+=x&-x;}
int ask(int x){int ans=0;while(x)ans+=c[x],x-=x&-x;return ans;}
void work()
{
    read(n); read(m);
    for(int i=1;i<=n;i++) read(a[i]),p[a[i]]=i;
    for(int i=1;i<=m;i++) read(que[i].l),read(que[i].r),que[i].id=i;
    for(int i=1;i<=n;i++)
    {
        for(int j=a[i]+a[i];j<=n;j+=a[i])
            if (i<p[j])vec[p[j]].push_back(i);
            else vec[i].push_back(p[j]);
    }
    sort(que+1,que+m+1);
    int r=0;
    for(int i=1;i<=m;i++)
    {
        for(int j=r+1;j<=que[i].r;j++)
            for(int k=0;k<vec[j].size();k++)change(vec[j][k]);
        r=que[i].r;
        ans[que[i].id]=ask(que[i].r)-ask(que[i].l-1);
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("aa.in","r",stdin);
#endif
    work();
}

转载于:https://www.cnblogs.com/mmmqqdd/p/11508422.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值