2023ACM暑假训练day 7 RMQ问题
DAY 7 RMQ问题
训练地址:传送门
训练情况简介
2023-07-03 星期一
早上:ABC
下午:DE、F(无思路)
晚上:F(题解思路+题解纠错)、G(题解思路)
2023-07-03 22:30:06 星期一
下机了,今天早上st表模板,下午敲熟了模板和二维的运用,晚上练了FG两道st表的运用,总的来说还是有点收获的
ABst一维板子
CD二维题
E需要优化
FG可以再自己试试
A题
题意:
st表模板题
思路:
不知道为啥,一直在RE
后面改着改着,a了,控制变量法提交,发现是cin和快读一起用的问题?(不太懂)
B 题
题意:
st表模板题
思路:
st表模板题
C 题
题意:
给你一个$ n*n $的矩阵,和一个数b,共k次询问,每次询问包含两个数x和y,问你以坐标(x,y)与坐标(x+b-1,y+b-1)为矩阵顶点的子矩阵内部元素的最大值减最小值
思路:
二维st表
D 题
hdu 2888 Check Corners
题意:
给你一个矩阵,多次问你子矩阵的最大值,并且判断子矩阵的最大值是不是在矩阵四角处
思路:
与C题类似,但是注意空间,容易超
题目限32768 kB,我30488K险过(汗)
E 题
hdu 3486 Interviewe
题意:
给你一个长度为n的数组,从前往后按照给定的顺序分组,取出每个组的最大值并求和,要求这个和大于给定的数k。若存在这样的分组,使分组数最小;若不存在,输出-1
思路:
利用st表维护区间最大值,但是分组求和判断这一操作如果仅从1开始到n判断组数的话,容易超时,需要进行优化。
优化一:统计数组的最大值Max,那么\(k/Max\)就是最小的分组数,因为至少需要这么多个最大值才有可能和大于k,更何况还不一定有这么多的最大值Max
优化二:在分组求和时,若当前sum已经大于k了,则立即退出求和,因为当前分组情况已经满足条件,继续求和纯属浪费时间,尤其是组数较多时!
做的时候还MLE了,其实这题int就够了,不需要开long long,开int都17128K了,题目限制32768 kB,long long就很浪费了
具体见代码
//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long
inline ll read()
{
ll x=0,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-48;ch=getchar();}
return x*f;
}
using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
一维st表配上二分答案?
又双叒叕错了
TLE了
MLE了
*/
const int maxm=200000+5,inf=0x3f3f3f3f,mod=998244353,Logn=19;
int n,k,f[maxm][Logn],logn[maxm];
void pre_logn(){
logn[1]=0;logn[2]=1;
for(int i=3;i<maxm;++i) logn[i]=logn[i/2]+1;
return ;
}
void pre_f(){
int c=logn[n]+1;
for(int j=1;j<c;++j){
for(int i=1;i+(1<<j)-1<=n;++i){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
return ;
}
void solve(){
pre_logn();
while(cin>>n>>k){
if(n==-1) break;
int sum=0,Max=0;//记得赋初值
for(int i=1;i<=n;++i){
cin>>f[i][0];
Max=max(Max,f[i][0]);//求取最大值
sum+=f[i][0];
}
if(sum<=k){//一人一组都不行
cout<<-1<<'\n';
continue;
}
pre_f();
/*
分为m组,每组n/m个人
上面已经求得了最大值Max
则c代表至少需要几个这样的最大值,才有可能使sum超过k
还是TLE....
继续优化?
从前开始,不加完了,前面都大了,那整体肯定大了
*/
int ans=n,c=k/Max;
if(c==0) c=1;
for(int i=c;i<=n;++i){
sum=0;
int len=n/i,lg,x,y;
lg=logn[len];
y=0;x=1-len;
for(int j=1;j<=i;++j){
x+=len;y+=len;
sum+=max(f[x][lg],f[y-(1<<lg)+1][lg]);
if(sum>k) break;
}
if(sum>k){
ans=i;
break;
}
}
cout<<ans<<'\n';
}
return ;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
F 题 题解思路
hdu 3193 Find the hotel
题意:
给你n个旅馆的价格 p 和距离 c ,让你判断有多少个旅馆,满足没有旅馆的价格和距离都严格小于当前旅馆,最后将所有符合条件的旅馆以旅馆价格递增,距离递增的方式输出
思路:
首先输入所有旅馆的信息,再按照价格升序排序,之后对于每一个旅馆,目前价格已经升序了,我们只需要判断对于价格小于当前旅馆的部分,是否也存在距离小于当前旅馆的,若不存在,则当前旅馆就是一个符合要求的旅馆
而如何快速判断呢?静态区间最值问题,可以利用st表求解,这还是个裸的st表
之后注意一点,严格小于的事情。就是在利用st表询问的时候,我们需要注意,我们需要在询问价格严格小于当前旅馆的所在区间,这个区间判断错了,即使距离判出小也是无用的。这就是自己最后一个错误的原因(虽然发现严格小于了,但是没注意,自己判的时候没完全保证两个要素均严格小于)
相关资料:
思路及最后的判断部分改错思路来源:传送门
G 题 题解思路
hdu 3183 A Magic Lamp
题意:
给你一个蛮长的数字和你可以操作的次数m,你一共可以在给定的数字里删除m个数字,要你找一种删除方法,使得剩下的数字按照原顺序排列时形成的数组最小(剩下的数字忽略前导0)
思路:
咱就是说,直接在原数组进行删除操作,不好维护吧也不方便,自己刚开始的删除写的奇奇怪怪的,运行都不起来
之后就搜了题解,转换思维,要在长度为n的数字中删去m个字符使得剩下的数字最小,那么其实也就是让你取n-m个数字,按照原顺序排序,使其最小。之后我们可以注意到,第一个字符一定在原数字[1,m+1]位(从前开始数)取得,设位置为x,则第二位数字则在[x+1,m+2]中取得...(此处详见下面的链接)
至于为啥?你第一位都取到后面去了,那后面留下的字符都不够了,意味着你删多了!所以这种说法是合理的
那么剩下的问题就是在我们想要的区间里面快速找到数字最小的那一个,静态区间最值问题,st表上即可。但是为了方便,这里\(f[maxm][logn]\)需要存的是给定区间中原数字中最小数字的下标,之后可以重载一下min使得整体样式与上面的st表代码相同。
详见代码:
//>>>Qiansui
#include<map>
#include<set>
#include<list>
#include<stack>
#include<cmath>
#include<queue>
#include<deque>
#include<cstdio>
#include<string>
#include<vector>
#include<utility>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x,y,sizeof(x))
#define debug(x) cout << #x << " = " << x << endl
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << endl
//#define int long long
inline ll read()
{
ll x=0,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-48;ch=getchar();}
return x*f;
}
using namespace std;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ull,ull> pull;
typedef pair<double,double> pdd;
/*
这题,怎么贪心的做呢?
或者说,和RMQ问题有什么关系?
逆向思维,删除一个数并不容易,但是呢,添加一个数简单啊!
*/
const int maxm=1e3+5,inf=0x3f3f3f3f,mod=998244353,Logn=10;
string ss,ans;
int n,logn[maxm],f[maxm][Logn];
int min(int x,int y){//重载min,使之与本题适配
return ss[x-1]<=ss[y-1]? x:y;
}
void pre_logn(){
logn[1]=0;logn[2]=1;
for(int i=3;i<maxm;++i) logn[i]=logn[i/2]+1;
return ;
}
void pre_f(){
int lg,len=ss.size();
lg=logn[len]+1;
for(int j=1;j<lg;++j){
for(int i=1;i+(1<<j)-1<=len;++i){
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
return ;
}
int query(int l,int r){
int lg=logn[r-l+1];
return min(f[l][lg],f[r-(1<<lg)+1][lg]);
}
void solve(){
pre_logn();
while(cin>>ss){
cin>>n;
int t,len=ss.size();
for(int i=1;i<=len;++i){//注意下标的修改!!!
f[i][0]=i;//输入ss的下标
}
pre_f();
ans="";
t=1;
for(int i=1;i<=len-n;++i){
t=query(t,n+i);//找到前面的最小,放在最终的答案中
ans+=ss[t-1];
++t;//后面添加的一定在当前之后
}
int pos=0;
len=ans.size();
for(;pos<len;++pos){//排除前导0的影响
if(ans[pos]!='0') break;
}
if(pos==len){//忽略全零的情况
cout<<"0\n";
continue;
}
cout<<ans.substr(pos)<<'\n';
}
return ;
}
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _=1;
// cin>>_;
while(_--){
solve();
}
return 0;
}
相关资料:
https://blog.csdn.net/acdreamers/article/details/8692384