数据结构·ST表
【YbtOj】题解
A.数列区间
静态查询区间最值,ST表板子。
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m;
int a[N];
int lg[N],st[N][30];
void initST()
{
lg[0]=-1;
for (int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
for (int i=1;i<=n;i++) st[i][0]=a[i];
for (int j=1;j<=lg[n];j++)
{
for (int i=1;i+(1<<j)-1<=n;i++) st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
signed main()
{
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
initST();
int l,r;
while (m--)
{
scanf("%lld%lld",&l,&r);
int k=lg[r-l+1];
printf("%lld\n",max(st[l][k],st[r-(1<<k)+1][k]));
}
return 0;
}
B.静态区间
板子上进行一个小小的变形即可,ST表维护当前区间\(gcd\)
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+5;
int n,m;
int a[N];
int lg[N],st[N][30];
int gcd(int x,int y)
{
if (!y) return x;
return gcd(y,x%y);
}
void initST()
{
lg[0]=-1;
for (int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
for (int i=1;i<=n;i++) st[i][0]=a[i];
for (int j=1;j<=lg[n];j++)
{
for (int i=1;i+(1<<j)-1<=n;i++)
st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
signed main()
{
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
initST();
int l,r;
while (m--)
{
scanf("%lld%lld",&l,&r);
int k=lg[r-l+1];
printf("%lld\n",gcd(st[l][k],st[r-(1<<k)+1][k]));
}
return 0;
}
C.与众不同
待补
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
const int M=1e6+5;
int n,m;
int a[N];
int pre[N],lst[M<<1],len[N];
int lg[N],st[N][30];
void init()
{
for (int i=1;i<=n;i++)
{
pre[i]=max(pre[i-1],lst[a[i]+1000000]+1);
len[i]=i-pre[i]+1;
lst[a[i]+1000000]=i;
st[i][0]=len[i];
}
lg[0]=-1;
for (int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
for (int j=1;j<=lg[n];j++)
{
for (int i=1;i+(1<<j)-1<=n;i++)
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
int find(int l,int r)
{
int res=l,x=l;
while (l<=r)
{
int mid=(l+r)>>1;
if (pre[mid]<x) l=mid+1,res=mid;
else r=mid-1;
}
return res;
}
signed main()
{
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
init();
int l,r;
while (m--)
{
scanf("%lld%lld",&l,&r);
l++,r++;
int mid=find(l,r);
int ans=mid-l+1;
if (mid<r)
{
int k=lg[r-mid];
ans=max(ans,max(st[mid+1][k],st[r-(1<<k)+1][k]));
}
printf("%lld\n",ans);
}
return 0;
}
D.矩阵最值
二维ST表板子。
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=255;
int n,m,q;
int a[N][N];
int lg[N],st[N][N][9][9];
void initST()
{
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) st[i][j][0][0]=a[i][j];
for (int k1=0;k1<=lg[n];k1++)
{
for (int k2=0;k2<=lg[m];k2++)
{
if (!k1&&!k2) continue;
for (int i=1;i+(1<<k1)-1<=n;i++)
{
for (int j=1;j+(1<<k2)-1<=m;j++)
{
if (k1) st[i][j][k1][k2]=max(st[i][j][k1-1][k2],st[i+(1<<(k1-1))][j][k1-1][k2]);
else st[i][j][k1][k2]=max(st[i][j][k1][k2-1],st[i][j+(1<<(k2-1))][k1][k2-1]);
}
}
}
}
}
signed main()
{
lg[0]=-1;
for (int i=1;i<N;i++) lg[i]=lg[i>>1]+1;
scanf("%lld%lld%lld",&n,&m,&q);
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) scanf("%lld",&a[i][j]);
initST();
int x1,y1,x2,y2;
while (q--)
{
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
int k1=lg[x2-x1+1],k2=lg[y2-y1+1];
int max1=max(st[x1][y1][k1][k2],st[x2-(1<<k1)+1][y1][k1][k2]);
int max2=max(st[x1][y2-(1<<k2)+1][k1][k2],st[x2-(1<<k1)+1][y2-(1<<k2)+1][k1][k2]);
printf("%lld\n",max(max1,max2));
}
return 0;
}
E.接水问题
注意到要最少的可以满足要求的长度,一眼二分。二分查找一个长度为\(len\)的区间,每个区间的时间间隔就是\(max_{len}-min_{len}\),先用ST表预处理出最值,每次check时再\(O(n)\)扫一遍所有长度为\(len\)的区间即可。
贴
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int M=1e6+5;
const int inf=0x3f3f3f3f;
int n,d,L;
int st1[M][20],st2[M][20];
void initST()
{
for (int j=1;j<=log2(L);j++)
{
for (int i=0;i+(1<<j)-1<=L;i++)
{
st1[i][j]=min(st1[i][j-1],st1[i+(1<<(j-1))][j-1]);
st2[i][j]=max(st2[i][j-1],st2[i+(1<<(j-1))][j-1]);
}
}
}
bool check(int len)
{
int res=0;
int k=log2(len+1);
for (int i=0;i+len-1<=L;i++)
{
int j=i+len-1;
int mn=min(st1[i][k],st1[j-(1<<k)+1][k]);
int mx=max(st2[i][k],st2[j-(1<<k)+1][k]);
if (mn!=inf&&mx!=0&&mx!=mn) res=max(res,mx-mn);
}
return res>=d;
}
signed main()
{
freopen("flowerplot.in","r",stdin);
freopen("flowerplot.out","w",stdout);
memset(st1,0x3f,sizeof st1);
scanf("%d%d",&n,&d);
for (int i=1,x,h;i<=n;i++)
{
scanf("%d%d",&x,&h);
L=max(L,x);
st1[x][0]=min(st1[x][0],h),st2[x][0]=max(st2[x][0],h);
}
initST();
int l=1,r=L+1;
while (l<r)
{
int mid=(l+r)>>1;
if (check(mid+1)) r=mid;
else l=mid+1;
}
if (l==L+1) printf("-1");
else printf("%d",l);
fclose(stdin);
fclose(stdout);
return 0;
}
F.方阵问题
注意到要是建普通的二维ST表,空间可能会爆炸。注意到“每个询问的方阵的较长边不超过较短边的两倍”,也就是说每个查询的矩阵一定可以被两个全等的正方形完全覆盖。于是,ST表可以减少一维,维护以\((i,j)\)为左上端点、边长为\(2^{k}\)的正方形内的信息。每次对正方形进行操作时,四个角取一遍即可。
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=805;
int n,m,a[N][N];
int q;
int lg[N];
int mn[N][N][11],mx[N][N][11],sum[N][N];
void initST()
{
lg[0]=-1;
for (int i=1;i<=max(n,m);i++) lg[i]=lg[i>>1]+1;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
mn[i][j][0]=mx[i][j][0]=a[i][j];
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
}
}
for (int k=1;k<=lg[min(n,m)];k++)
{
for (int i=1;i+(1<<k)-1<=n;i++)
{
for (int j=1;j+(1<<k)-1<=m;j++)
{
int res=min(mn[i][j][k-1],mn[i+(1<<(k-1))][j][k-1]);
res=min(res,mn[i][j+(1<<(k-1))][k-1]);
mn[i][j][k]=min(res,mn[i+(1<<(k-1))][j+(1<<(k-1))][k-1]);
res=max(mx[i][j][k-1],mx[i+(1<<(k-1))][j][k-1]);
res=max(res,mx[i][j+(1<<(k-1))][k-1]);
mx[i][j][k]=max(res,mx[i+(1<<(k-1))][j+(1<<(k-1))][k-1]);
}
}
}
}
int query_max(int x1,int y1,int x2,int y2)
{
int k=lg[min(x2-x1,y2-y1)+1];
int mx1=max(mx[x1][y1][k],mx[x2-(1<<k)+1][y1][k]);
int mx2=max(mx[x1][y2-(1<<k)+1][k],mx[x2-(1<<k)+1][y2-(1<<k)+1][k]);
return max(mx1,mx2);
}
int query_min(int x1,int y1,int x2,int y2)
{
int k=lg[min(x2-x1,y2-y1)+1];
int mn1=min(mn[x1][y1][k],mn[x2-(1<<k)+1][y1][k]);
int mn2=min(mn[x1][y2-(1<<k)+1][k],mn[x2-(1<<k)+1][y2-(1<<k)+1][k]);
return min(mn1,mn2);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) cin>>a[i][j];
initST();
cin>>q;
string op;
int x1,x2,y1,y2;
while (q--)
{
cin>>op>>x1>>y1>>x2>>y2;
x1++,x2++,y1++,y2++;
if (op=="SUM")
{
int ans=sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
cout<<ans<<endl;
continue;
}
if (op=="MAX")
{
if (x2-x1>y2-y1) cout<<max(query_max(x1,y1,x1+(y2-y1),y2),query_max(x2-(y2-y1),y1,x2,y2))<<endl;
else cout<<max(query_max(x1,y1,x2,y1+(x2-x1)),query_max(x1,y2-(x2-x1),x2,y2))<<endl;
}
if (op=="MIN")
{
if (x2-x1>y2-y1) cout<<min(query_min(x1,y1,x1+(y2-y1),y2),query_min(x2-(y2-y1),y1,x2,y2))<<endl;
else cout<<min(query_min(x1,y1,x2,y1+(x2-x1)),query_min(x1,y2-(x2-x1),x2,y2))<<endl;
}
}
return 0;
}
G.降雨量
我宣布,这就是史
对于“\(Y\)年是自\(X\)年以来降雨量最多的”,我们进行一个分类讨论
- 若 \(X\geqslant Y\) ,输出 \(false\)
- 若 \(X,Y\) 同时未知,输出 \(maybe\)
- 若 \(X,Y\) 知道一个,那么按照题意输出 \(maybe\) 或 \(false\)
- 若 \(X,Y\) 都知道
- 若 \(X,Y\) 之间的都知道,那么按照题意输出 \(true\) 或 \(false\)
- 否则,按照题意输出 \(maybe\) 或 \(false\)
其中,判断一个年份是否已知可以用 \(map\) 维护,判断是否符合题意可以用ST表维护最大值判断。
但要注意神奇的边界问题,以防取到上界又+1后判断发生错误,刚开始要往 \(map\) 里插入一个超大的数,赋值为 \(n+1\)(害我调试近一天的罪魁祸首!!)
贴
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+5;
int n,m;
int y[N],r[N];
map <int,int> mp;
int lg[N],st[N][30];
void initST()
{
lg[0]=-1;
for (int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
for (int i=1;i<=n;i++) st[i][0]=r[i];
for (int j=1;j<=lg[n];j++)
{
for (int i=1;i+(1<<j)-1<=n;i++) st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld%lld",&y[i],&r[i]);
mp[y[i]]=i;
}
mp[1e9+7]=n+1;
initST();
scanf("%lld",&m);
int x,y;
while (m--)
{
scanf("%lld%lld",&x,&y);
if (x>=y) { printf("false\n"); continue; }
if (mp.find(x)==mp.end()&&mp.find(y)==mp.end()) { printf("maybe\n"); continue; }
if ((mp.find(x)==mp.end()&&mp.find(y)!=mp.end())||(mp.find(y)==mp.end()&&mp.find(x)!=mp.end()))
{
int val,pos1,pos2;
if (mp.find(x)!=mp.end())
{
pos1=mp[x];
val=r[pos1],pos1++;
pos2=(mp.lower_bound(y))->second;
pos2--;
}
else
{
pos2=mp[y];
val=r[pos2],pos2--;
pos1=(mp.lower_bound(x))->second;
}
if (pos1>pos2) { printf("maybe\n"); continue; }
int k=lg[pos2-pos1+1];
int ans=max(st[pos1][k],st[pos2-(1<<k)+1][k]);
if (ans<val) printf("maybe\n");
else printf("false\n");
}
else
{
if (r[mp[y]]>r[mp[x]]) { printf("false\n"); continue; }
int pos1=mp[x]+1,pos2=mp[y]-1;
if (pos1>pos2)
{
if (y-x==1) printf("true\n");
else printf("maybe\n");
continue;
}
int k=lg[pos2-pos1+1];
int ans=max(st[pos1][k],st[pos2-(1<<k)+1][k]);
if (ans<r[pos2+1])
{
if (pos2-pos1+2==y-x) printf("true\n");
else printf("maybe\n");
}
else printf("false\n");
}
}
return 0;
}