P10833 [COTS 2023] 下 Niz

storms11發表於2024-11-06

題目連結

主要演算法

分治(最大值分治),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;
}

相關文章