拓扑AC NOIP模拟赛1

题出的不错,但是测试时限与下发时限不同,怎么会是呢?

T1

题目链接

先考虑弱化版。

对于答案,一定有其中一个位置的高度被用满。

所以枚举这个位置,在考虑这个位置被占满时的最大答案。

发现只需要找出这个位置之前第一个 \(<\) 它的高度的位置和它之后第一个 \(<\) 它的高度的位置即可。

这个直接两遍单调栈就做完了。

强化版中可以将一个位置变为 \(1\sim H\) 中的任意高度。

先不使用,做一遍弱化版求出目前最大值。

如果使用的话,变成 \(H\) 一定不劣。

发现答案中还是一定有一个位置高度被用满,接着枚举这个位置。

但是由于可以将一个位置变为 \(H\) ,所以我们还需要知道上上个 \(<\) 当前高度的位置和下下个 \(<\) 当前高度的位置。

把这些查询都离线下来,扫描线+权值树状数组即可。

时间复杂度 \(O(n\log n)\) ,跑的没暴力跳快。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10,inf=1e18;
int n,H,ans,h[N],w[N],s[N],st[N],top,pre1[N],pre2[N],suf1[N],suf2[N];
int cnt,c1[N],c2[N];
struct node{
	int pos,id;
	bool operator<(const node&x)const{
		return pos<x.pos;
	}
}qr[N];
int lowbit(int x){
	return x&-x;
};
void add1(int x,int k){
	while(x<N){
		c1[x]=max(c1[x],k);
		x+=lowbit(x);
	}
}
int query1(int x){
	int res=0;
	while(x){
		res=max(res,c1[x]);
		x-=lowbit(x);
	}
	return res;
}
void add2(int x,int k){
	while(x<N){
		c2[x]=min(c2[x],k);
		x+=lowbit(x);
	}
}
int query2(int x){
	int res=inf;
	while(x){
		res=min(res,c2[x]);
		x-=lowbit(x);
	}
	return res;
}
bool cmp(node x,node y){
	return x.pos>y.pos;
}
signed main(){
	freopen("poster.in","r",stdin);
	freopen("poster.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>H;
	for(int i=1;i<=n;i++){
		cin>>h[i]>>w[i];
		ans=max(ans,H*w[i]);
		s[i]=s[i-1]+w[i];
	}
	s[n+1]=s[n];
	for(int i=1;i<=n;i++){
		while(top&&h[st[top]]>=h[i]) top--;
		pre1[i]=st[top];
		if(pre1[i]>1) qr[++cnt]=(node){pre1[i]-1,i};
		else pre2[i]=0;
		st[++top]=i;
	}
	sort(qr+1,qr+cnt+1);
	int j=0;
	for(int i=1;i<=cnt;i++){
		while(j<qr[i].pos){
			j++;
			add1(h[j],j);
		}
		pre2[qr[i].id]=query1(h[qr[i].id]-1);
	}
	top=0;
	cnt=0;
	st[++top]=n+1;
	for(int i=n;i>=1;i--){
		while(top&&h[st[top]]>=h[i]) top--;
		suf1[i]=st[top];
		if(suf1[i]<n) qr[++cnt]=(node){suf1[i]+1,i};
		else suf2[i]=n+1;
		st[++top]=i;
	}
	memset(c2,0x3f,sizeof c2);
	sort(qr+1,qr+cnt+1,cmp);
	j=n+1;
	add2(1,n+1);
	for(int i=1;i<=cnt;i++){
		while(j>qr[i].pos){
			j--;
			add2(h[j],j);
		}
		suf2[qr[i].id]=query2(h[qr[i].id]-1);
	}
	for(int i=1;i<=n;i++) ans=max(ans,(s[suf1[i]-1]-s[pre1[i]])*h[i]);
	for(int i=1;i<=n;i++) ans=max(ans,max((s[suf2[i]-1]-s[pre1[i]])*min(h[i],H),(s[suf1[i]-1]-s[pre2[i]])*min(h[i],H)));
	cout<<ans<<'\n';
}

T2

题目链接

场上想到了正解,但是由于 \(O(nq\log n)\) 跑大样例只跑了 \(1.5s\) 于是没有进一步优化,喜提 \(50pts\)

先思考一下一个区间的最小代价到底是什么。

首先对区间排序,然后分奇偶讨论一下。

若区间长度为偶数,那就是将 \(i\)\(n-i+1\) 配对后的最大的和。

若区间长度为奇数,取出最大数单独分组,剩下的按偶数的操作,最后取最大值。

现在的问题是求一个序列的所有代价 \(\leq x\) 的区间的第一个数减去最后一个数的差的和。

题目中保证了给定的序列是不降的。

发现固定左端点后,区间代价随右端点的增加而单调不降。

于是只要对于每个左端点求出第一个不合法的右端点即可。

称这个答案为 \(res\) 数组。

直接枚举左端点然后去算右端点是不好做的,怎么办?

每个区间的代价是被其中和 \(>x\) 的限制了,所以我们可以用和 \(>x\) 的组来限制每个左端点的答案。

具体的,枚举组中的左端点 \(i\),求出与它匹配的第一个不合法右端点 \(b_i\),然后可以做以下限制:

\(len=\min(i,n-b_i+1)\), 则对 \(i-len+1\sim i\)\(res\) 分别对 \(b_i+len-1\sim b_i\)\(\min\)

注意要保证这两个位置是匹配的,即保证区间奇偶性。

我不会用数据结构维护这个东西,但是发现将所有斜线都补到 \(1\) 的位置不影响答案,而且补齐后桶排扫一遍就行了。

还需要考虑一下奇数长度区间的限制,即对所有 \(>x\) 的位置做一遍后缀 \(\min\) 即可。

前面算 \(b\) 数组可以用双指针实现。

于是在 \(O(nq)\) 内做完了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,inf=1e18;
int n,q,b[N],res[N],cnt;
int a[N],s[N],e[N<<1];
signed main(){
	freopen("match.in","r",stdin);
	freopen("match.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		s[i]=s[i-1]+a[i];
	}
	while(q--){
		int x;
		cin>>x;
		for(int i=1;i<=n;i++){
			b[i]=0;
			res[i]=n+1;
			e[i]=0;
		}
		for(int i=n+1;i<=(n<<1);i++) e[i]=0;
		cnt=0;
		int j=n;
		for(int i=1;i<=n;i++){
			while(j&&a[i]+a[j-1]>x) j--;
			if(a[i]+a[j]>x) b[i]=j;
			if(j<i) b[i]=i+1;
		}
		for(int i=1;i<=n;i++){
			if(!b[i]) continue;
			if((b[i]-i+1)&1) b[i]++;
			if(b[i]>n) continue;
			e[b[i]+i-1]=max(e[b[i]+i-1],i);
		}
		j=0;
		for(int i=1;i<=(n<<1);i++){
			if(e[i]>j){
				for(int k=j+1;k<=e[i];k++) res[k]=min(res[k],i-k+1);
				j=e[i];
			}
		}
		int tmp=inf;
		for(int i=n;i>=1;i--){
			if(a[i]>x) tmp=i;
			res[i]=min(res[i],tmp);
		}
		int ans=0;
		for(int i=1;i<=n;i++){
			if(res[i]<=i) continue;
			ans+=s[res[i]-1]-s[i-1];
			ans-=(res[i]-i)*a[i];
		}
		cout<<ans<<'\n';
	}
}

T3

目前不会

T4

目前不会

posted @ 2024-11-04 16:41  Hugoi  阅读(174)  评论(3)    收藏  举报