数据结构·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;
}

H.超级钢琴

I.二叉查找树

posted @ 2024-11-25 23:29  还是沄沄沄  阅读(9)  评论(0编辑  收藏  举报