欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

佳木斯市第一中学NOIP真题模拟赛(19.8.24—19.8.25)游记

程序员文章站 2024-03-18 22:07:52
...

NOIP2013真题测试,先简单总结一下成绩。
Day1 180分
一般般的成绩,T1A掉,T2依靠玄学算法搞到了70分,T3的10分就很惨了,想到了LCA却没有继续往下想最大生成树,还是比较菜的
Day2 210分
Day2貌似来手感了
T1,T2A掉,T3还是只有可怜巴巴的10分,暴力广搜真的没有get到,打开一看就跑去写dfs了
总分 390分
个人认为还算可以,毕竟是一年的蒟蒻 ,看了下当年的分数线,发现并不是很高,然而tf告诉我现在考分数线最少300分……emmmmmm心里一凉。

Day 1

T1 转圈游戏

T1瞄一眼,10^9,搞毛啊,仔细一看居然是道快速幂水题,速度十分钟搞完,调试一下,出了几组数据搞一搞,就完事了
代码简洁,相信一看就明白了

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m,k,x;
ll qpow(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=(ans*a)%n;
		a=(a*a)%n; b>>=1;
	}
	return ans;
}
int main()
{
	scanf("%lld%lld%lld%lld",&n,&m,&k,&x);
	int t=qpow(10,k);
	int res=(m*t)%n;
	printf("%lld",(x+res)%n);
	return 0;
}

其实没必要开long long ,考场上比较谨慎还是选择开了long long
T2 火柴排队

考场上的玄学方法,显然我们需要找两组数中相差最小的,每次对于第一组数a[i]中第i个数在第二组数b[i]中寻找与a[i]相差最小的数的位置,记为pos,每次移动时,需要将i到pos中所有的数位置向后移动一位,时间复杂度O(n^2),期望得分60pts,实际得分70pts

代码我忘记存了

满分算法:使a[i]中第k大的数与b[i]中第k大的数放在同一位置,即为最优答案

T3 货车运输

考场上想到了LCA,但发现是一个图就没再继续想,正解是最大生成树+LCA,最大生成树可以用并查集的Kruskal,将边权改为从大到小排序即可,LCA使用倍增LCA即可,在预处理倍增LCA时可预处理出minn[u][i],表示以u为起点,向上跳2^i次所经过最小的限重,在LCA时可以同时处理出路径上的最优解。

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define maxm 100005
#define maxn 100005
#define INF 0x3f3f3f3f 
int head[maxn],f[maxn],ff[maxn][25],deep[maxn],minn[maxn][25],vis[maxn];
int n,m,q,cnt=0;
struct node
{
	int x,y,val;
}ee[maxm];
struct Node
{
	int to,next,w;
}e[maxn*2];
inline int read()
{
	int w=1,s=0; char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')w=-1; ch=getchar();}
	while(ch>='0' && ch<='9'){s=s*10+ch-'0'; ch=getchar();}
	return w*s;
}
int find(int x)
{
	if(x==f[x]) return x;
	return f[x]=find(f[x]);
}
bool cmp(node a,node b)
{
	return a.val>b.val;
}
void add(int x,int y,int v)
{
	cnt++;
	e[cnt].to=y;
	e[cnt].w=v;
	e[cnt].next=head[x];
	head[x]=cnt;
}
void kruskal()
{
	for(int i=1;i<=n;i++) f[i]=i;
	sort(ee+1,ee+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		int x=ee[i].x,y=ee[i].y,v=ee[i].val;
		int rx=find(x),ry=find(y);
		if(rx!=ry)
		{
			f[rx]=ry;
			add(x,y,v); add(y,x,v);
		}
	}
}
void dfs(int u)
{
	vis[u]=1;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to,dis=e[i].w;
		if(vis[v]) continue;
		ff[v][0]=u; deep[v]=deep[u]+1; minn[v][0]=dis;
		dfs(v);
	}
}
int LCA(int x,int y)
{
	if(find(x)!=find(y)) return -1;
	int res=INF;
	if(deep[x]<deep[y]) swap(x,y);
	for(int i=22;i>=0;i--) 
	{
		if(deep[ff[x][i]]>=deep[y])
		{
			res=min(minn[x][i],res); x=ff[x][i];
		}
	}
	if(x==y) return res;
	for(int i=22;i>=0;i--)
	{
		if(ff[x][i]!=ff[y][i])
		{
			res=min(res,min(minn[x][i],minn[y][i]));
			x=ff[x][i]; y=ff[y][i];
		}
	}
	return min(res,min(minn[x][0],minn[y][0]));
}
int main()
{
	n=read(); m=read();
	for(int i=1;i<=m;i++)
	{
		ee[i].x=read(); ee[i].y=read(); ee[i].val=read();
	}
	kruskal();
	for(int i=1;i<=n;i++)
	{
		if(!vis[i]) {deep[i]=0; dfs(i); ff[i][0]=i; minn[i][0]=INF;}
	}
	for(int i=1;i<=22;i++)
	{
		for(int u=1;u<=n;u++)
		{
			ff[u][i]=ff[ff[u][i-1]][i-1];
			minn[u][i]=min(minn[ff[u][i-1]][i-1],minn[u][i-1]);
		}
	}
	q=read();
	while(q--)
	{
		int x=read(),y=read();
		printf("%d\n",LCA(x,y));
	}
	return 0;
}

Day 2

T1 积木大赛

考场上:这题我见过!
作为去年刚学两个月参加NOIP2018的我对第一题印象深刻,唯一得到70分的题,当时暴力找最长区间进行修改,时间复杂度O(n^2),期望得分:70分,实际得分:70分
正解:可以发现每次处理一个位置时,若此位置比前一位置小,那么此位置必定能被前一位置顺带处理,若此位置比前一位置大,那么在处理完前一位置后还需要加上剩余的次数,即(h-now)次(now 前一位置高度,h 当前高度),所以想法很简单了,从头到尾扫一遍,记录前一位置和当前位置,若当前位置大于前一位置,那么ans+=(h-now)

代码如下

#include <bits/stdc++.h>
using namespace std;
inline int read()
{
	int w=1,s=0; char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')w=-1; ch=getchar();}
	while(ch>='0' && ch<='9'){s=s*10+ch-'0'; ch=getchar();}
	return w*s;
}
int main()
{
	int n;
	n=read();
	int ans=0,now=0;
	ans=read(); now=ans;
	for(int i=1;i<n;i++)
	{
		int h=read();
		if(now<h) ans+=h-now;
		now=h;
	}
	printf("%d",ans);
}	

T2 花匠

题目中说的十分复杂,但是我们画图理解后可以发现,人话题目就是从长度为n的数列中取出m个数,使任意除去首尾的其他数要么满足当前数大于左右两边的数,或者满足当前数小于左右两边的数,画图可以知道,这题就是让我们求波峰和波谷的个数,flag记录当前递增或是递减,发现当前数是波峰或波谷时ans++,并改变flag,为了最大化m,首尾数字必区,所以需要加2,当然我在循环中处理了最后的数字,所以只需要+1

代码如下

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000005
int h[maxn],n;
inline int read()
{
	int w=1,s=0; char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')w=-1; ch=getchar();}
	while(ch>='0' && ch<='9'){s=s*10+ch-'0'; ch=getchar();}
	return w*s;
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++) h[i]=read();
	bool flag=0;
	int ans=0;
	if(h[2]>=h[1]) con=1;
	for(int i=2;i<=n;i++)
	{
		if(flag==0 && i==n) {ans++; break;}
		if(flag==1 && h[i+1]<h[i]){ans++; flag=0; continue;}
		else if(flag==0 && h[i+1]>h[i]){ans++; flag=1; continue;}
	}
	printf("%d",ans+1);
}

T3 华容道

考试真是脑子抽了,dfs居然用来求最短路径
暴力分给的很足,然而我还是没拿到 ,广搜即可,因为挪动棋子可以变相看作挪动空格,所以每次广搜空格上、下、左、右的格子,广搜深度即最终答案,期望得分:70分

正解:广搜+最短路
这里的最短路推荐使用SPFA,比较好理解

spfa最短路 起点可能不在图中,再跑bfs连边。最后的目标状态就是指定棋子在tx,ty,然后枚举空格子在tx,ty的上下左右求最小。