P10833 [COTS 2023] 下 Niz
题目链接
主要算法
分治(最大值分治),st表
思路
1.因为我们考虑最主要的限制条件是最大值和排列,所以如果我们知道最大值就知道答案的长度。所以考虑按最大值分治,统计左边对右边的贡献。
2.接下来就是如何快速考虑一个区间是否合法,一个显然的是没有相同数,所以可以记前一个数的位置的最大值,如果小于左端点就符合条件。同时我们发现这样还恰好满足了每个数都出现的要求(因为固定了最大值,所以只能出现1-max)。
3.所以用两个st表记最大值即可
#include <bits/stdc++.h>
using namespace std;
const int N=2e6+10;
#define ll long long
int n,g[N][21],las[N],lg[N];//不要压线开
struct node
{
int fi,se;
}f[N][21];//同时存位置和最大值
ll ans;
node maxx(node x,node y)
{
return x.fi>=y.fi?x:y;
}
node query1(int l,int r)
{
int cd=lg[r-l+1];
return maxx(f[l][cd],f[r-(1<<cd)+1][cd]);
}
int query2(int l,int r)
{
int cd=lg[r-l+1];
return max(g[l][cd],g[r-(1<<cd)+1][cd]);
}
void solve(int l,int r)
{
if(l>r)return ;
int mx=query1(l,r).fi,mxz=query1(l,r).se,mid=n+1;//必须直接找到中点,保证时间复杂度
if(mxz-l<=r-mxz)//选小的才能保证时间复杂度
for(int i=max(mxz-mx+1,l);i<=mxz;i++)
{
int j=i+mx-1;
if(j>r)break;
if(query2(i,j)<i)ans++;
}
else
for(int j=mxz;j<=min(mxz+mx-1,r);j++)
{
int i=j-mx+1;
if(i<l)continue;
if(query2(i,j)<i)ans++;
}
solve(l,mxz-1);
solve(mxz+1,r);
return ;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int x;cin>>x;
f[i][0]=(node){x,i};
g[i][0]=las[x];
las[x]=i;
}
lg[0]=-1;
for(int i=1;i<=n;i++)lg[i]=lg[i/2]+1;
for(int j=1;j<=lg[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++)
f[i][j]=maxx(f[i][j-1],f[i+(1<<(j-1))][j-1]),g[i][j]=max(g[i][j-1],g[i+(1<<(j-1))][j-1]);
solve(1,n);
cout<<ans<<'\n';
return 0;
}
相同思路的题:P4755 Beautiful Pair
最大值分治+主席树秒了!
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
#define ll long long
int n,cnt,st[N][18],a[N],ls[31*N],rs[31*N],rt[N],lg[N],mx;
ll ans,sh[31*N];
int query(int x,int l,int r,int k)
{
if(l>k||l>r||!x)return 0;
if(r<=k&&l>=1)return sh[x];
int mid=(l+r)>>1;
ll sum=0;
sum+=query(ls[x],l,mid,k);
sum+=query(rs[x],mid+1,r,k);
return sum;
}
int querymax(int l,int r)
{
int cd=lg[r-l+1];
return a[st[l][cd]]>=a[st[r-(1<<cd)+1][cd]]?st[l][cd]:st[r-(1<<cd)+1][cd];
}
void solve(int l,int r)
{
if(l>r)return ;
int wz=querymax(l,r);
// cout<<l<<" "<<r<<" "<<wz<<'\n';
if(wz-l<r-wz)
{
for(int i=l;i<=wz;i++)
{
int ned=a[wz]/a[i];
ans+=query(rt[r],1,mx,ned)-query(rt[wz-1],1,mx,ned);
}
}
else
{
for(int i=wz;i<=r;i++)
{
int ned=a[wz]/a[i];
ans+=query(rt[wz],1,mx,ned)-query(rt[l-1],1,mx,ned);
}
}
solve(l,wz-1);solve(wz+1,r);
}
void modify(int oldx,int &newx,int l,int r,int wz)
{
newx=++cnt;ls[newx]=ls[oldx],rs[newx]=rs[oldx];
if(l==r)
{
sh[newx]=sh[oldx]+1;
return ;
}
int mid=(l+r)>>1;
if(wz<=mid)modify(ls[oldx],ls[newx],l,mid,wz);
else modify(rs[oldx],rs[newx],mid+1,r,wz);
sh[newx]=sh[ls[newx]]+sh[rs[newx]];
return ;
}
int main()
{
cin>>n;
lg[0]=-1;
for(int i=1;i<=n;i++){cin>>a[i];mx=max(a[i],mx);}
for(int i=1;i<=n;i++)
{
st[i][0]=i;
modify(rt[i-1],rt[i],1,mx,a[i]);
lg[i]=lg[i/2]+1;
}
for(int j=1;j<=lg[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++)
st[i][j]=a[st[i][j-1]]>=a[st[i+(1<<(j-1))][j-1]]?st[i][j-1]:st[i+(1<<(j-1))][j-1];
solve(1,n);
cout<<ans<<'\n';
return 0;
}