题解 P4755 【Beautiful Pair】

题解 - P 4755 \mathrm{P4755} P4755

题目意思

题目传送门

S o l \mathrm{Sol} Sol

一道套路题,很多题都用到了这个套路。但由于主席树的总总原因调了好久。。。

首先我们用单调栈 O ( n ) O(n) O(n) 求出对于一个数 i i i ,左右两边第一个比他大的数的位置为 l i l_i li r i r_i ri

那么我们就要用到一个套路就是每次我们枚举长度较短的一边来计算长的一边,这样子均摊下来是 log ⁡ n \log n logn 的。于是假设我们现在在 i i i 的左区间内枚举到 j j j ,那么我们就要在右区间理找出有几个 a k a_k ak 满足 a k ≤ a i a j a_k\leq \frac{a_i}{a_j} akajai。这个就是个经典问题啦,直接用主席树维护区间 [ l , r ] [l,r] [l,r] 中有几个数小于 x x x

总体来说时间复杂度为 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

C o d e \mathrm{Code} Code

#include 
#define For(i,a,b) for ( int i=(a);i<=(b);i++ )
#define Dow(i,b,a) for ( int i=(b);i>=(a);i-- )
#define GO(i,x) for ( int i=head[x];i;i=e[i].nex )
#define mem(x,s) memset(x,s,sizeof(x))
#define cpy(x,s) memcpy(x,s,sizeof(x))
#define YES return puts("YES"),0
#define NO return puts("NO"),0
#define GG return puts("-1"),0
#define pb push_back
#define int long long  
using namespace std;

struct IO
{
	#define MAXSIZE (1 << 20)
	#define isdigit(x) (x >= '0' && x <= '9')
  	char buf[MAXSIZE],*p1,*p2;
  	char pbuf[MAXSIZE],*pp;
  	IO():p1(buf),p2(buf),pp(pbuf){}
  	inline char gc() {
    	if (p1==p2) p2=(p1=buf)+fread(buf,1,MAXSIZE,stdin);
    	return p1==p2?' ':*p1++;
  	}
	inline bool blank(char ch) {return ch==' '||ch =='\n'||ch == '\r'||ch == '\t';}
  	template <class T>
  	inline void read(T &x)
	{
	    register double tmp=1;register bool sign=0; x=0;
	    register char ch=gc();
	    for (;!isdigit(ch);ch=gc()) if(ch=='-') sign=1;
	    for (;isdigit(ch);ch=gc()) x=x*10+(ch-'0');
	    if (ch=='.') for (ch=gc();isdigit(ch);ch=gc()) tmp/=10.0,x+=tmp*(ch-'0');
	    if (sign) x=-x;
  	}
  	inline void read(char *s)
	{
    	register char ch=gc();
		for (;blank(ch);ch=gc());
		for (;!blank(ch);ch=gc()) *s++=ch;
    	*s=0;
  	}
  	inline void read(char &c) {for(c=gc();blank(c);c=gc());}
  	template<class t> inline void write(t x){
		if(x<0) putchar('-'),write(-x);
		else{if(x>9) write(x/10);putchar('0'+x%10);}
	}
} io;

const int mod=1e9+7;
const int mo=998244353;
const int N=1e5+5;
const int M=5e6+1; 

int n,m,l[N],r[N],stak[N],a[N],b[N],Rt[N],top,ds;

struct Seg
{
	int ls,rs,sum;
};
Seg T[N*30];

inline void insert(int pre,int &p,int l,int r,int pos)
{
	p=++ds;
	T[p]=T[pre];
	T[p].sum++;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(pos<=mid) insert(T[pre].ls,T[p].ls,l,mid,pos);
	else insert(T[pre].rs,T[p].rs,mid+1,r,pos);
}

inline int ask(int pre,int p,int l,int r,int ql,int qr)
{
	if(ql>r||qr<l) return 0;
	if(l>=ql&&r<=qr) return T[p].sum-T[pre].sum;
	int mid=(l+r)>>1;
	int res=0;
	if(ql<=mid) res+=ask(T[pre].ls,T[p].ls,l,mid,ql,qr);
	if(qr>mid) res+=ask(T[pre].rs,T[p].rs,mid+1,r,ql,qr);
	return res;
}

signed main()
{
	io.read(n);
	For(i,1,n) 
	{
		io.read(a[i]);
		b[i]=a[i];
		r[i]=n,l[i]=1;
	}
	sort(b+1,b+n+1);
	m=unique(b+1,b+n+1)-b-1;
	stak[top=1]=0;
	For(i,1,n) 
	{
		while(top&&a[stak[top]]<a[i]) top--;
		if(top) l[i]=stak[top]+1;
		stak[++top]=i;
	}
	stak[top=1]=n+1;
	Dow(i,n,1)
	{
		while(top&&a[stak[top]]<=a[i]) top--;
		if(top) r[i]=stak[top]-1;
		stak[++top]=i;
	}
	For(i,1,n) 
	{
		int p=lower_bound(b+1,b+m+1,a[i])-b;
		insert(Rt[i-1],Rt[i],1,m,p);
	}
	int ans=0;
	For(i,1,n) 
	{
		if(i-l[i]<r[i]-i)
		{
			For(j,l[i],i) 
			{
				int rem=(ceil)(a[i]/a[j]);
				int p=upper_bound(b+1,b+m+1,rem)-b-1;
				ans+=ask(Rt[i-1],Rt[r[i]],1,m,1,p);
			}
		}
		else 
		{
			For(j,i,r[i])
			{
				int rem=(ceil)(a[i]/a[j]);
				int p=upper_bound(b+1,b+m+1,rem)-b-1;
				int gs=0;
				ans+=ask(Rt[l[i]-1],Rt[i],1,m,1,p);
			}
		}
	}
	io.write(ans);
	return 0;
}

你可能感兴趣的:(树状数组,思维,主席树)