2019 The Preliminary Contest for ICPC Asia Xuzhou 2019 I题

思路当然来自我们的京学长咯(膜拜巨佬)(代码也基本一样。。),这是他的博客,里面有很多有用的东西:https://mmfunnytree.github.io/2050/01/18/AlgorithmTemplate/#more

回到思路:因为题目要满足min(a[j],a[i])= gcd(a[j],a[i]),所以肯定有一个数是另外一个的因数

因为这是个区间问题,我首先想到线段树,但是单纯的线段树肯定不行,我们得建立a[i]和a[j]的关系

我们可以用 类似埃筛的方法 : 对于 j 这个数 如果它的倍数和它自己在查询区间L到R之间的话,那么对答案就有1贡献

所以 我们可以 利用主席树对于每个j的倍数(不包括j本身) 我们把j的位置的权值+1,当询问区间L到R时答案就是R和L-1这两颗线段树在区间L到R的 权值差之和(一颗树K维护的是从区间1到K的答案)

可能讲的有点稀巴烂,有什么错误请指出(555 太菜了)

#include<bits/stdc++.h>
/*
题意:求区间[l,r]内满足min(a[i],a[j])=gcd(a[i],a[j])(l<=i<j<=r)的对数
*/ 
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
vector<int> yz[maxn];
int n,m;
int a[maxn],pa[maxn];
int T[maxn],lson[maxn*200],rson[maxn*200];
int c[maxn*200];
int tot;
void update(int &u,int p,int l,int r){
    ++tot;lson[tot]=lson[u];rson[tot]=rson[u];c[tot]=c[u]+1;
    u=tot;
    if(l==r)return;
    int mid=l+r>>1;
    if(p<=mid)update(lson[u],p,l,mid);
    else      update(rson[u],p,mid+1,r);
}
int query(int lrt,int rrt,int L,int R,int l,int r){
    if(L<=l&&R>=r){
        return c[rrt] - c[lrt];
    }int mid = l+r>>1,ret=0;
    if(L<=mid)ret+=query(lson[lrt],lson[rrt],L,R,l,mid);
    if(R>mid) ret+=query(rson[lrt],rson[rrt],L,R,mid+1,r);
    return ret;
}
int main(){
    ++tot;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d",a+i);
        pa[a[i]]=i;
    }
    for(int i = 1; i <= n; i++){
    	for(int j = 2*i; j <= n; j+=i){
    		yz[j].push_back(pa[i]);
		}
	}
    for(int i=1;i<=n;++i){
        T[i]=T[i-1];
        for(auto v:yz[a[i]]){
            update(T[i],v,1,n);
        }
    }
    int l,r;
    while(m--){
        scanf("%d%d",&l,&r);
        printf("%d\n",query(T[l-1],T[r],l,r,1,n));
    }
    return 0;
}

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值