题目
给定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;
}