Luogu8251 solution

Problem

link->https://www.luogu.com.cn/problem/P8251

Solution

这道题我考场上最后 \(10\) 分钟想出正解,然而在考试完后的 \(10\) 分钟才打出代码。。。

可以发现,无论什么情况,下标大的二元组总是在下标小的二元组的上面,也就是说,如果一个二元组它不是“完美的”,那么它一定是被一个下标小于它的二元组所堵住。

由于 \(l\)\(r\) 是依次入栈的,那么如果一个二元组不是完美的,那么它只可能被一个唯一确定的二元组挡住。

那么,我们可以先求出,如果在区间为 \([1,1]\) 状态下,每个二元组是被哪个二元组所挡住的,记为 \(f_i\)\(f_i=0\),则它是“完美的”)。

stack<int> s;
_for(i,1,n) { //循环宏,定义见完整代码
	while(!s.empty()&&!(a[s.top()]!=a[i]&&b[i]<b[s.top()])) s.pop();
	if(!s.empty()) f[i]=s.top();
	//printf("f[%d] = %d\n",i,f[i]);
	s.push(i);
}

为什么上述代码正确地求出 \(f_i\) 呢?我们不妨来用反证法证明一下:

\(x\) 同样能挡住 \((a_i,b_i)\)(是最优的 \(f_i\)),但是 \(x\) 一定在 \(f_i\) 之下,而 \(f_i\) 一定能挡住 \((a_i,b_i)\),故 \(f_i\)\(x\) 之前就能挡住 \((a_i,b_i)\),是最优的 \(f_i\)

容易看出,在 \([l,r]\) 区间中,如果下标为 \(i\) 的二元组 \((a_i,b_i)\),是完美的,当且仅当 \(f_i<l\),即唯一确定挡住它的二元组不在 \([l,r]\) 区间内。

while(q--) {
	int l,r;
	read(l,r); //快读,定义见完整代码
	int ans=0;
	_for(i,l,r)
		if(f[i]<l)
			++ans;
	printf("%d\n",ans);
}

不过,上述代码时间复杂度在最坏情况任是 \(\Theta(nm)\),无法通过此题。

我们不妨先看看现在的问题:

给定无序数组 \(\{f_n\}\),进行 \(m\) 次每次给定 \(l,r\) 问有多少个 \(f_i<l\)\(l\le i\le r\)

看见有很多大佬用主席树在线查询的,蒟蒻我不禁瑟瑟发抖。

我们设为 \(f(l,r,k)\) 为满足 \(f_i<k\)\(l\le i\le r\)\(f_i\) 数量,那么运用一些前缀和的思想,可以看出 \(f(l,r,l)=f(1,r,l)-f(1,l,l)+1\)

对于所有的 \(f(1,r,k)\) 我们均可以使用树状数组以 \(\Theta(n\log{n})\) 的时间复杂度一次性计算出来。

这样,我们不妨离线处理出每个 \(f(1,r,l)\)\(f(1,l,l)\),最后回答即可。

Code

#include<bits/stdc++.h>
#define pd push_back
#define pb pop_back
#define mk make_pair
#define LL long long
#define PII pair<int,int>
#define _for(a,b,c) for(int a=b;a<=c;a++)
#define _rep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template <typename T> inline void read(T& x) {
	x=0; T f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch&15); ch=getchar(); }
	x=x*f;
	return;
}
template <typename T,typename ...Arg> inline void read(T& x,Arg& ...arg){
	read(x); read(arg...);
}
int power(int a,int b) {
	int ans=1;
	do {
		if(b&1) ans*=a; a*=a;
	} while(b>>=1);
	return ans;
}
const int N=5e5+5;
int a[N],b[N],f[N],n,q;
int tree[N],ans[N];
struct node {
	int l,r,id;
}; node que[N];
bool cmp1(node x,node y) {
	return x.l<y.l;
}
bool cmp2(node x,node y) {
	return x.r<y.r;
}
int lowbit(int x) { return x&-x; }
void Add(int x,int k) {
	while(x<=n)
		tree[x]+=k,x+=lowbit(x);
}
int Query(int x) {
	int sum=0;
	while(x)
		sum+=tree[x],x-=lowbit(x);
	return sum;
}
int main() {
//	freopen("stack.in","r",stdin);
//	freopen("stack.out","w",stdout);
	read(n,q);
	_for(i,1,n)
		read(a[i]);
	_for(i,1,n)
		read(b[i]);
	stack<int> s;
	_for(i,1,n) {
		while(!s.empty()&&!(a[s.top()]!=a[i]&&b[i]<b[s.top()])) s.pop();
		if(!s.empty()) f[i]=s.top();
		//printf("f[%d] = %d\n",i,f[i]);
		s.push(i);
	}
	_for(i,1,q) {
		int l,r;
		read(l,r);
		que[i]=node{l,r,i};
	}
	sort(que+1,que+q+1,cmp1);
	int k=1;
	_for(i,1,n) {
		Add(f[i]+1,1);
		//printf("f[%d]=%d++\n",i,f[i]);
		while(i==que[k].l) {
			//printf("%d %d %d\n",que[k].l,que[k].r,que[k].id);
			ans[que[k].id]-=Query(que[k].l),++k;
		}
	}
	sort(que+1,que+q+1,cmp2);
	memset(tree,0,sizeof(tree));
	k=1;
	_for(i,1,n) {
		Add(f[i]+1,1);
		//printf("f[%d]=%d++\n",i,f[i]);
		while(i==que[k].r) {
			//printf("%d %d %d\n",que[k].l,que[k].r,que[k].id);
			ans[que[k].id]+=Query(que[k].l),++k;
		}
	}
	_for(i,1,q)
		printf("%d\n",ans[i]+1);
	return 0;
}
posted @ 2022-08-01 14:12  lsj2009  阅读(30)  评论(0)    收藏  举报