题解
A - Balanced Lineup
题目:给定一组数,求指定区间的最大值和最小值。
- RMQ标准问题
- ST算法预处理及查询
- 对应代码
//简单应用模板,但是注意最小花查询初始化的时候也要新起一个函数,不然最小化求得不对,不晓得怎么回事。。。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50000+100;
int dmax[maxn][20], dmin[maxn][20];
void initmax(int n,int d[])//初始化最大值查询
{
for(int i=1; i<=n; i++)
dmax[i][0]=d[i];
for(int j=1 ; (1<<j)<=n ; j++)
for(int i=1; i+(1<<j)-1 <=n; i++)
dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
}
int getmax(int L,int R)//查询最大值
{
int k=0;
while((1<<(k+1))<=R-L+1)k++;
return max(dmax[L][k], dmax[R-(1<<k)+1][k]);
}
void initmin(int n,int d[])
{
for(int i=1; i<=n; i++)
dmin[i][0]=d[i];
for(int j=1; (1<<j)<=n; j++)
for(int i=1; i+(1<<j)-1<=n; i++)
dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);
}
int getmin(int L,int R)
{
int k=0;
while( (1<<(k+1)) <=R-L+1)k++;
return min(dmin[L][k],dmin[R-(1<<k)+1][k]);
}
int main()
{
int n, t;
scanf("%d%d", &n, &t);
int a[maxn];
for(int i=1; i<=n; i++)
scanf("%d", &a[i]);
initmax(n, a); initmin(n, a);
while(t--)
{
int A, B;
scanf("%d%d", &A, &B);
int mx = getmax(A, B);
int mn = getmin(A, B);
printf("%d\n", mx-mn);
}
return 0;
}
B - A Magic Lamp
题目:读入一个整数,不改变顺序,删掉数位上m个数,留下的数字形成一个新的数,求这个数最小是多少
- RMQ+思维
- 思路:
- 分析:如178543,删掉4个数,留下的二位数的第一个数字一定在原数的前5位(4+1)里面,也就是在17854里面找第一个数,之后,从找到的1的下一位7开始,在78543这个区间里找。
- 做法:ST算法循环查找。
- 获得一个
char
类型数组的长度:int len = strlen(ch)
- 对应代码:
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<string.h>
using namespace std;
const int MAXN=1010;
int a[MAXN];//a是把字符转成数字的
int dp[MAXN][20];
char str[MAXN];
int ans[MAXN];
void initmin(int n,int b[])//形成最小值下标的RMQ
{
for(int i=0; i<n; i++)
dp[i][0]=i;
for(int j=1; (1<<j)<=n; j++)
for(int i=0; i+(1<<j)-1<n; i++)
dp[i][j]=b[dp[i][j-1]] <= b[dp[i+(1<<(j-1))][j-1]] ? dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
//这里一定要加等号,就是相等的时候取下标小的
}
int rmqIndex(int L,int R,int b[])
{
int k=0;
while( (1<<(k+1)) <=R-L+1)k++;
return b[dp[L][k]] <= b[dp[R-(1<<k)+1][k]] ? dp[L][k]:dp[R-(1<<k)+1][k];
//加等号,取小标小的
}
int main()
{
int m;
while(scanf("%s%d",&str,&m)!=EOF)
{
int n=strlen(str);
for(int i=0; i<n; i++) a[i]=str[i]-'0';
initmin(n,a);
int pos=0, flag=0;
for(int i=m; i<n; i++) //找n-m个数,每次从[t,i]中找最小的
{
pos=rmqIndex(pos,i,a);
ans[flag++]=a[pos++];
}
//去前导零
pos=0;
while(pos<flag&&ans[pos]==0) pos++;
if(pos>=flag)printf("0\n");
else
{
for(int i=pos; i<flag; i++)printf("%d",ans[i]);
printf("\n");
}
}
return 0;
}
C - Maximum Absurdity
题目:在一组数里面里选出两个不交叉的长度为k的区间,使两个区间的值的和最大,输出两个区间第一个数的坐标。
- 枚举吧。。感觉很经典
- 设一个很大的数:
#define INF 0x3f3f3f3f
,设一个很小的数可以:-INF
(前面的INF) - 代码部分
//枚举。。
#include<iostream>
#include<string.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 2e5 + 10;
ll a[maxn], ans[maxn];
int main()
{
ll n, k;
cin >> n >> k;
memset(a, 0, sizeof a);
memset(ans, 0, sizeof ans);
for(int i=1; i<=n; i++)
{
cin >> a[i];
a[i] = a[i] + a[i-1];//处理一下使每个储存的是它前面所以数字的和
}
for(int i=1; i<= n-(k-1); i++)
ans[i] = a[i-1 + k] - a[i-1];
ll L, R, pos;
ll min1 = -INF, min2 = -INF;
for(int i=n-(k-1); i-k>=1; i--)
{
if(min1 <= ans[i])
{
min1 = ans[i];
pos = i;
}
if(min2 <= min1 + ans[i-k])
{
min2 = min1 + ans[i-k];
L = i - k;
R = pos;
}
}
cout << L << " " << R << endl;
return 0;
}
E - The Water Problem
题目:给一组数,求指定区间里面最大的数。
- RMQ
- ST算法,直接用A题的模板就行,只需要处理一下输入输出。
F-P1890 gcd区间
题目:给一组数,求出指定区间的最大公因数
- RMQ变化
- 把ST算法里的max换成gcd函数
- gcd函数
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}