8.17模拟赛小结

发布时间 2023-08-17 16:37:38作者: g1ove

前言

最卡常的一集

T1 激光通讯 原题

题意:给你一个大小不超过 \(100\times 100\) 的矩阵 其中有一个起点,终点和一些障碍物 求从起点到终点不碰到障碍物的最小转弯次数

思考 一开始肯定是想记忆化 dfs 但是那样写了下发现麻烦 于是 改成了 bfs
容易发现转弯次数能小就小 所以将普通的队列改成一个堆即可
时间复杂度 \(O(n^2\log n^2)\)

Code

#include<bits/stdc++.h>
#define N 105
using namespace std;
int n,m,sx,sy,ex,ey,ans=1e9;
char c[N][N];
int vis[N][N][2];//0横1竖 
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
struct point {
	int x,y,w,v;
};
bool operator < (point a,point b)
{
	return a.v>b.v;
}
priority_queue <point> q; 
void bfs()
{
	q.push((point){sx,sy,0,0});
	q.push((point){sx,sy,1,0});
	while(!q.empty())
	{
		point x=q.top();
		q.pop();		
		if(x.x>n||x.y>m||x.x<1||x.y<1||c[x.x][x.y]=='*') continue;
		if(x.x==ex&&x.y==ey)
			ans=min(ans,x.v);
		vis[x.x][x.y][x.w]=1;
		for(int i=0;i<2;i++)
		{
			int xx=x.x+dx[i],yy=x.y+dy[i];
			if(vis[xx][yy][0]) continue;
			q.push((point){xx,yy,0,x.v+x.w});
		}
		for(int i=2;i<4;i++)
		{
			int xx=x.x+dx[i],yy=x.y+dy[i];
			if(vis[xx][yy][1]) continue;
			q.push((point){xx,yy,1,x.v+1-x.w});
		}
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>m>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			cin>>c[i][j];
			if(c[i][j]=='C')
				if(sx==0) sx=i,sy=j;
				else ex=i,ey=j;
		}
	bfs();
	cout<<ans;
	return 0;
}

T2 树

题意:已知一棵二叉树点的编号为 \(1\to n\) 且中序遍历也为 \(1\to n\) 给出这棵树的层次遍历 求这棵树的先序遍历

思考:了解过平衡树 BST 堆之类的就知道 原树就是一个 BST 因此满足左儿子小于根 右儿子大于根的性质 因此每个子树都必定是一个区间 然后这个子树的根就是层次遍历最靠前的点 于是用一棵线段树维护即可 时间复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
#define N 300005
using namespace std;
int n,a[N],id[N];//id:每个数的下标 
int tr[4*N];
void Pushup(int x)
{
	if(id[tr[x*2]]>id[tr[x*2+1]]) tr[x]=tr[x*2+1];
	else tr[x]=tr[x*2];
}
void builds(int l,int r,int x)
{
	if(l==r)
	{
		tr[x]=l;
		return;
	}
	int mid=(l+r)/2;
	builds(l,mid,x*2);
	builds(mid+1,r,x*2+1);
	Pushup(x);
}
int query(int l,int r,int L,int R,int x)
{
	if(l>R||r<L) return -1;
	if(l>=L&&r<=R) return tr[x];
	int mid=(l+r)/2;
	int ls=query(l,mid,L,R,x*2),rs=query(mid+1,r,L,R,x*2+1);
	if(ls==-1) return rs;
	if(rs==-1) return ls;
	if(id[ls]>id[rs]) return rs;
	return ls;
}
void solve(int l,int r)
{
	if(l>r) return;
	int root=query(1,n,l,r,1);
	printf("%d ",root);
	solve(l,root-1);
	solve(root+1,r);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]),id[a[i]]=i;
	builds(1,n,1);
	solve(1,n);
	return 0;
}

T3 石头剪刀布

image
\(n\leq 2000\)

很毒瘤
错误的思路:前\(i\)个数最长上升序列长度为\(j\) 钦定每次最后取的数是 \(k\) 的方案数
因为这道题要求的是全局 所以不能钦定