ST表

思路

\(ST\) 表可以在 \(O(n\log n)\) 的预处理以后 \(O(1)\) 的解决 \(RMQ\) 问题,并且不能带修。

\(RMQ\) 问题有这样一个特质:重复计算并不会影响最终结果,例如计算一个点最大值的两次并不会改变最终的区间最大值,因此 \(ST\) 表的作法也由此而生。首先 \(n\log n\) 预处理出每一个点后长度为 \(2^k,k\in \mathbb{N^+},2^k+i\leq n\) 的最大值,合并时只需合并最恰好大于等于区间一半长度的长度即可。

ST表

思路

版题。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int a[100005];
int lg[100005];
int maxn[100005][25];
int n, m, l, r;

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q))	x = (x<<1) + (x<<3) + (q^48), q = getchar();
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0)	putchar('-'), x = -x;
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

int main(){
    read(n), read(m);
    lg[0] = -1;
    for(register int i = 1; i <= n; ++i){
        read(a[i]);
        maxn[i][0] = a[i];
        lg[i] = lg[i/2]+1;
    }
    for(register int i = 1; i <= lg[n]; ++i)
        for(register int j = 1; j+(1<<i)-1 <= n; ++j)
			maxn[j][i] = max(maxn[j][i-1], maxn[j+(1<<(i-1))][i-1]);
    while(m--){
        read(l), read(r);
        int len = lg[r-l+1];
        write(max(maxn[l][len], maxn[r-(1<<(len))+1][len]));
        putchar('\n');
    }
    return 0;
}

Balanced Lineup G

思路

维护一个 \(maxn\) 和一个 \(minn\)\(st\) 表线段树都可以。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int a[100005];
int lg[100005];
int maxn[50005][25], minn[50005][25];
int n, m, l, r;

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q))	x = (x<<1) + (x<<3) + (q^48), q = getchar();
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0)	putchar('-'), x = -x;
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

int main(){
    read(n), read(m);
    lg[0] = -1;
    for(register int i = 1; i <= n; ++i){
        read(a[i]);
        maxn[i][0] = a[i];
        minn[i][0] = a[i];
        lg[i] = lg[i/2]+1;
    }
    for(register int i = 1; i <= lg[n]; ++i)
        for(register int j = 1; j+(1<<i)-1 <= n; ++j)
			maxn[j][i] = max(maxn[j][i-1], maxn[j+(1<<(i-1))][i-1]), minn[j][i] = min(minn[j][i-1], minn[j+(1<<(i-1))][i-1]);
    while(m--){
        read(l), read(r);
        int len = lg[r-l+1];
        write(max(maxn[l][len], maxn[r-(1<<(len))+1][len])-min(minn[l][len], minn[r-(1<<(len))+1][len]));
        putchar('\n');
    }
    return 0;
}

3.[USACO07JAN] Balanced Lineup G

思路

版题但是 \(\min \max\) 同时处理版本。

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int a[50005];
int lg[50005];
int maxn[50005][25], minn[50005][25];
int n, m, l, r;

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q))	x = (x<<1) + (x<<3) + (q^48), q = getchar();
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0)	putchar('-'), x = -x;
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

int main(){
    read(n), read(m);
    lg[0] = -1;
    for(register int i = 1; i <= n; ++i){
        read(a[i]);
        maxn[i][0] = a[i];
        minn[i][0] = a[i];
        lg[i] = lg[i/2]+1;
    }
    for(register int i = 1; i <= lg[n]; ++i)
        for(register int j = 1; j+(1<<i)-1 <= n; ++j)
			maxn[j][i] = max(maxn[j][i-1], maxn[j+(1<<(i-1))][i-1]), minn[j][i] = min(minn[j][i-1], minn[j+(1<<(i-1))][i-1]);
    while(m--){
        read(l), read(r);
        int len = lg[r-l+1];
        write(max(maxn[l][len], maxn[r-(1<<(len))+1][len])-min(minn[l][len], minn[r-(1<<(len))+1][len])), puts("");
    }
    return 0;
}

P7809 [JRKSJ R2] 01 序列

思路

对于问题一,这是可以记录一个 \(0\) 的前缀和,一个 \(1\) 的后缀和,可以得知对于点 \(i\)\(ans=pre_{i}-pre_{l-1}+suf_{i}-suf_{r+1}\)。该部分直接使用 \(ST\) 表即可实现。当然,这里会存在两种特殊情况,即全为 \(0\) 或者全为 \(1\),需特判即可。

对于问题二是比较好处理的,记录下每个位置最近的前面的 \(1\) 的与每个位置最近的后面的 \(0\),在区间中如果 \(pre0_l<pre1_r\),即说明存在一组 \(01\)

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, m;
bool a[1000005];
int op, l, r;
int pre[1000005], lg[1000005];			//pre为前缀和
int pre1[1000005], pre0[1000005];		//pre1为前方的第一个1位置,pre0为后方的第一个0位置
int maxn[2000005][25];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

int main(){
	read(n), read(m);
	lg[0] = -1;
	for(register int i = 1; i <= n; ++i){
		read(a[i]);
		pre[i] = pre[i-1]+a[i];
		lg[i] = lg[i>>1]+1;
		if(a[i])	pre1[i] = i;
		else	pre1[i] = pre1[i-1];
		maxn[i][0] = i-2*pre[i];
	}
	pre0[n+1] = n+1;
	for(register int i = n; i >= 1; --i){
		if(!a[i])	pre0[i] = i;
		else	pre0[i] = pre0[i+1];
	}
	for(register int i = 1; i <= lg[n]; ++i)
		for(register int j = 1; j <= n; ++j)
			maxn[j][i] = max(maxn[j][i-1], maxn[j+(1<<i-1)][i-1]);
	for(register int i = 1; i <= m; ++i){
		read(op), read(l), read(r);
		if(op == 1){
			write(max(max(pre[r]-pre[l-1], (r-l+1)-(pre[r]-pre[l-1])), max(maxn[l][lg[r-l+1]], maxn[r-(1<<lg[r-l+1])+1][lg[r-l+1]])-l+1+pre[r]+pre[l-1])), puts("");
		}
		if(op == 2){
			if(pre0[l]<pre1[r])	puts("2");
			else	puts("1");
		}
	}
	return 0;
}
posted @ 2024-10-09 18:16  Zzzzzzzm  阅读(3)  评论(0)    收藏  举报