题目链接
题意:
给你一个长度为 n n n的序列,问存在多少对 ( i , j ) (i,j) (i,j),满足 a i ∗ a j < = m a x { a i , a i + 1 . . . a j } a_i*a_j<=max\{a_i,a_{i+1}...a_{j}\} ai∗aj<=max{ai,ai+1...aj}。也就是有多少个位置对满足序列上的值的乘积小于等于区间最大值。 n < = 1 e 5 n<=1e5 n<=1e5。
题解:
这个题想了半天,感觉可能作为切入点的还是这个最大值。我们的一个想法是找到当前的区间最大值,然后分治。当前层统计跨过最大值的答案,不跨过的递归到两个子区间。这样对于随机数据是对的,因为最大值期望的出现位置比较接近中间。但是精心构造的话,递归层数会变成 O ( n ) O(n) O(n)量级,可能每一层处理一遍都要 O ( n ) O(n) O(n)或者 O ( n l o g n ) O(nlogn) O(nlogn)的话,复杂度就不对了。
那么我们应该怎么办呢?这里就是这个题的关键所在了。对于一般的题目,对于最值分治一般是复杂度不对的,应该是只有在数据随机的情况下复杂度是对的。但是这个题不保证数据随机,就要想别的办法来保证复杂度了。我们考虑到,对于当前递归的区间,我们既可以枚举最大值前面的每个数,统计后面的一半有多少个数可以与前面形成合法的数对,也可以枚举后半部分的每一个数来考虑前面的数对后面的数的贡献。那么我们按照这样一个策略来枚举:我们看最值两侧哪一侧的数少,就枚举哪一侧,这样就能保证复杂度。原因是,每个数每被枚举一次,那么这个区间的大小就会至少增大到原来的两倍,所以每个数最多被枚举 l o g log log次,总枚举量是 n l o g n nlogn nlogn级别的。另外我们还要维护一个区间内在某个权值范围内的数的个数,这个东西可以用一个主席树来维护。
其他的都是一些常规操作了,不懂可以看看代码,我不想赘述了。
代码:
#include
using namespace std;
int n,a[100010],b[100010],cnt,root[100010],shu;
long long ans;
struct node
{
int l,r,mx,id;
}tr1[400010];
struct tree
{
int l,r,sz;
}tr2[2000010];
inline int read()
{
int x=0;
char s=getchar();
while(s>'9'||s<'0')
s=getchar();
while(s>='0'&&s<='9')
{
x=x*10+s-'0';
s=getchar();
}
return x;
}
inline void build1(int rt,int l,int r)
{
tr1[rt].l=l;
tr1[rt].r=r;
tr1[rt].mx=0;
tr1[rt].id=0;
if(l==r)
{
tr1[rt].mx=a[l];
tr1[rt].id=l;
return;
}
int mid=(l+r)>>1;
build1(rt<<1,l,mid);
build1(rt<<1|1,mid+1,r);
tr1[rt].mx=max(tr1[rt<<1].mx,tr1[rt<<1|1].mx);
if(tr1[rt<<1].mx>=tr1[rt<<1|1].mx)
tr1[rt].id=tr1[rt<<1].id;
else
tr1[rt].id=tr1[rt<<1|1].id;
}
inline void build2(int &rt,int l,int r)
{
if(!rt)
rt=++shu;
tr2[rt].sz=0;
if(l==r)
return;
int mid=(l+r)>>1;
build2(tr2[rt].l,l,mid);
build2(tr2[rt].r,mid+1,r);
}
inline void update(int &rt,int rt1,int l,int r,int x)
{
rt=++shu;
tr2[rt]=tr2[rt1];
tr2[rt].sz++;
if(l==r)
return;
int mid=(l+r)>>1;
if(x<=mid)
update(tr2[rt].l,tr2[rt1].l,l,mid,x);
else
update(tr2[rt].r,tr2[rt1].r,mid+1,r,x);
}
inline node query1(int rt,int le,int ri)
{
int l=tr1[rt].l,r=tr1[rt].r;
if(le<=l&&r<=ri)
return tr1[rt];
int mid=(l+r)>>1;
node res,ji1,ji2;
ji1.mx=0;
ji1.id=0;
ji1.l=0;
ji1.r=0;
ji2.mx=0;
ji2.id=0;
ji2.l=0;
ji2.r=0;
res.mx=0;
res.id=0;
res.l=0;
res.r=0;
if(le<=mid)
ji1=query1(rt<<1,le,ri);
if(mid+1<=ri)
ji2=query1(rt<<1|1,le,ri);
if(ji1.mx>=ji2.mx)
res=ji1;
else
res=ji2;
return res;
}
inline int query(int rt1,int rt2,int l,int r,int x)
{
if(r<=x)
return tr2[rt1].sz-tr2[rt2].sz;
int mid=(l+r)>>1,res=0;
res+=query(tr2[rt1].l,tr2[rt2].l,l,mid,x);
if(mid+1<=x)
res+=query(tr2[rt1].r,tr2[rt2].r,mid+1,r,x);
return res;
}
inline void solve(int l,int r)
{
if(l>r)
return;
if(l==r)
{
if(b[a[l]]==1)
++ans;
return;
}
node ji=query1(1,l,r);
int mid=ji.id,mx=b[ji.mx];
if(r-mid>mid-l+1)//枚举前面
{
for(int i=l;i<=mid;++i)
{
int x=upper_bound(b+1,b+cnt+1,mx/b[a[i]])-b-1;
if(x)
ans+=query(root[r],root[mid-1],1,cnt,x);
}
}
else//枚举后面
{
for(int i=mid;i<=r;++i)
{
int x=upper_bound(b+1,b+cnt+1,mx/b[a[i]])-b-1;
if(x)
ans+=query(root[mid],root[l-1],1,cnt,x);
}
}
solve(l,mid-1);
solve(mid+1,r);
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
a[i]=read();
b[i]=a[i];
}
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-b-1;
for(int i=1;i<=n;++i)
a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
build1(1,1,n);
build2(root[0],1,cnt);
for(int i=1;i<=n;++i)
update(root[i],root[i-1],1,cnt,a[i]);
solve(1,n);
printf("%lld\n",ans);
return 0;
}