Codeforces Round #813 (Div. 2) E. LCM Sum (lcm 离线+树状数组)

这篇博客主要介绍了如何使用二项堆(BIT)优化处理大量在线询问的问题,特别是在处理互不相等的i、j、k满足lcm(i,j,k)>i+j+k的三元组数量时。博客提供了两种解决方案,一种适用于5组询问的简单情况,另一种则是针对1e5组询问的优化方法,通过离线处理和BIT实现O(qlogq+n(logn)^2)的时间复杂度。文章还讨论了如何从简单的思路扩展到更复杂的场景,并给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

题目

给定t(t<=1e5)组询问,每次给定l,r(1<=l<=r<=2e5,l+2<=r),

询问在[l,r]之间互不相等的i,j,k(i<j<k)满足lcm(i,j,k)>i+j+k的三元组(i,j,k)的数量

和easy的区别仅在于询问数量,easy是5组询问,允许离线

思路来源

看了红名爷的代码就会了

题解

首先直接统计不好统计,然后考虑用总的减去不满足条件的

对不满足条件的小范围打了个表,发现只有两种情况,

1. lcm(i,j,k)=k,即i|k且j|k,所以枚举k的约数统计

2. lcm(i,j,k)=2k,此时有i和j均是2k的因子且i和j在2的因子个数之和比k多一个的结论,可惜这个结论没啥用,进一步观察打表结果发现,只可能是(3,4,6)和(6,10,15)这两种情况及其倍数,这就可以做了

easy的时候搞了一个指针每次暴力扫到合法的位置,O(qnlogn)

hard就是把询问离线,指针扫的时候插到BIT上,相当于(i,j,k),枚举k使之不超过当前r的限制,把对于i来说当前满足j的个数插入在i的位置,查询时只查满足条件的段,O(qlogq+n(logn)^2)

BIT常见套路,然而看着1e5组询问,值域2e5,

赛中过了easy之后全在想怎么莫队,然后发现会转移右端点不会转移左端点,过于睿智

bonus:想想1log咋做

代码1(easy)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
typedef long long ll;
int t,l,r,L,R;
vector<int>fac[N];
void init(){
    for(int i=1;i<N;i++){
        for(int j=2*i;j<N;j+=i){
            fac[j].push_back(i);
        }
    }
}
ll C2(int x){
    return 1ll*x*(x-1)/2;
}
ll C3(int x){
    return 1ll*x*(x-1)*(x-2)/6;
}
int main(){
    init();
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&l,&r);
        ll ans=C3(r-l+1);
        for(int k=l+2;k<=r;++k){
            int id=0;
            while(id<fac[k].size() && fac[k][id]<l)id++;
            if(id>=fac[k].size())continue;
            int sz=fac[k].size()-id;
            ans-=C2(sz);
        }
        L=(l+2)/3,R=r/6;//(3,4,6)
        ans-=max(0,R-L+1);
        L=(l+5)/6,R=r/15;//(6,10,15)
        ans-=max(0,R-L+1);
        printf("%lld\n",ans);
    }
    return 0;
}

代码2(hard)

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
typedef long long ll;
int t,L,R,tr[N];
ll ans[N];
vector<int>fac[N];
struct node{
	int l,r,id;
}e[N];
bool operator<(node a,node b){
	return a.r<b.r;
}
void add(int x,int v){
	for(int i=x;i<N;i+=i&-i){
		tr[i]+=v;
	}
}
int sum(int x){
	int ans=0;
	for(int i=x;i;i-=i&-i){
		ans+=tr[i];
	}
	return ans;
}
void init(){
    for(int i=1;i<N;i++){
        for(int j=2*i;j<N;j+=i){
            fac[j].push_back(i);
        }
    }
}
ll C2(int x){
    return 1ll*x*(x-1)/2;
}
ll C3(int x){
    return 1ll*x*(x-1)*(x-2)/6;
}
int main(){
    init();
    scanf("%d",&t);
    for(int i=1;i<=t;++i){
    	scanf("%d%d",&e[i].l,&e[i].r);
    	e[i].id=i;
    }
    sort(e+1,e+t+1);
    int k=1;
    for(int i=1;i<=t;++i){
    	int id=e[i].id,l=e[i].l,r=e[i].r;
    	ans[id]=C3(r-l+1);
        for(;k<=r;++k){
        	int sz=fac[k].size();
        	for(int i=0;i<sz;++i){
        		add(fac[k][i],sz-1-i);
        	}
        }
        ans[id]-=sum(r)-sum(l-1);
        L=(l+2)/3,R=r/6;//(3,4,6)
        ans[id]-=max(0,R-L+1);
        L=(l+5)/6,R=r/15;//(6,10,15)
        ans[id]-=max(0,R-L+1);
    }
    for(int i=1;i<=t;++i){
    	printf("%lld\n",ans[i]);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值