前言:
这场比赛的题是大约一个月前出的,当时刚从NOIP2001-NOIP2011之间的题目比较顺利地走过来。当时感觉NOIP并不是很难,但在这一个月经历了很多,最近几年的NOIP瞬间让我意识到自己能力的不足,特别是在NOIP2012,NOIP2015中惨败,又见识了起步早,能力强的小学/初中大佬,这让学了15个月OI马上要参加最后一次NOIP的我倍感压力。
从近年NOIP的考试趋势来看,部分考察知识开始涉及省选或以前不常考内容,题目难度也有所上升。现在看这套题,我觉得题目风格偏旧,难度不足,有点像NOIP2011之前的考试风格。总之,有诸多不足,望见谅。如果有误,请指出。
比赛链接:https://www.luogu.org/contestnew/show/10364
T1:小凯的数字
注:关于下面引理的证明,我第一次了解是在某数学竞赛黑皮书上
考察知识:数学,数论,模拟
算法难度:XX+ 实现难度:X
评价:这还是算一道和NOIP切和很好的题目,数学类题目是近年NOIP必考题。
感想:我本来以为然而
分析:
这道题用类似高精度除法的方法可以做,但是肯定要超时。
正解和NOIP2017T1解法有点相似,也是推(cai)结(da)论(an)的题
结论:对于输入的l,r输出:
坑点:如果不先取模再乘会爆 long long
证明:
先证明一个引理:对于数
有:
注意到: 则
,
那么:
引理得证。
我们已经离结论很近了,如果我们单纯把 的数字进行分解还是要超时,所以我们还要继续优化:
同理我们可以证明:
即:
由等差数列求和公式:
结论得证。
T2:密室
注:背景来自某小说,有趣的是,今天(9.21)我在洛谷上看到了另一道题目背景相同的题(当然,只是题目背景相同),有兴趣可以看看:CF173B Chamber of Secrets
考察知识:图的最短路
算法难度:XX+ 实现难度:XXX
评价:非常常规,主要考察最短路的求法
感想:哎,出得太简单了,但是需要大家读懂题
分析:
没什么好分析的,用多次最短路+分类讨论就可以了。
情况一:哈利罗恩分别去一间密室
情况二:哈利一个人去两间密室
T3:PION贪吃蛇
注:游戏规则改编自游戏:slither.io
注意:对于部分数据(有蛇连在一起)我的代码可能会错误
忠告:模拟类题目(比如:侦探推理,mayan游戏)在NOIP还是经常考到的,而且描述大多也不是非常清晰,所以大家要仔细读题,自己推敲。
考察知识:模拟,队列,四连块搜索
算法难度:XXX+ 实现难度:XXX+
评价:如果数据强度够大,如果放到NOIP2011年以前,也可能算最后一道题的难度了,现在的话......
感想:好吧,也许题目描述确实有点杂乱,但是我说过数据很水。
分析:
注意题目数据范围中的限制:100%数据满足,n,m<=200,c<=20,k<=100,且图中的蛇不会引起混淆
首先题目数据范围很小,我们直接按要求模拟就可以了,其次蛇不会引起混淆,我们就可以用dfs标记每条蛇身的顺序,就可以模拟出蛇的移动了。
细节:
1.蛇身的储存:用队列实现,先dfs搜索将蛇加入队列:队首为蛇尾,队尾为蛇头
2.蛇的移动:队首出队列,将蛇头到达的地方加入队列
3.蛇的死亡:因为蛇为四连块,用dfs标记
4.其他细节见代码
附,数据可能存在(两条蛇不会混淆吧):
......
..@@..
......
但数据不会这么坑:
.#.
##@
#.#
@##
代码:
T1:
#include<iostream>
using namespace std;
long long Q,l,r,a,b;
int main(){
cin>>Q;
while(Q--){
cin>>l>>r;
a=r-l+1,b=l+r;//等差数列求和公式
if(a%2==0) a/=2;
else b/=2;
a%=9,b%=9;//分开求防止爆long long
cout<<(a*b)%9<<'\n';
}
return 0;
}
T2:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=100005,maxm=500005;
struct node{
int id,d;
friend bool operator < (node A,node B){
return A.d>B.d;
}
};
struct edge{
int to,next,c;
}e[maxm*2];
int head[maxn],np;
void add(int u,int v,int c){
e[++np]=(edge){v,head[u],c};
head[u]=np;
e[++np]=(edge){u,head[v],c};
head[v]=np;
}
bool vis[maxn];
int n,m,k,x,y,locked[maxn];
int d1[maxn],d2[maxn],d3[maxn];
//d1[i]:哈利由1到i的最短路
//d2[i]:罗恩由1到i的最短路
//d3[y]:哈利由x到y的最短路
void dijstra(int s,int* d,bool Access){
priority_queue<node>pq;
memset(vis,0,sizeof(vis));
d[s]=0;
pq.push((node){s,0});
int i;
while(!pq.empty()){
i=pq.top().id;pq.pop();
if(vis[i]) continue;
vis[i]=true;
for(int p=head[i];p;p=e[p].next){
int j=e[p].to;
if(!Access&&locked[j]) continue;
if(d[i]+e[p].c<d[j]){
d[j]=e[p].c+d[i];
if(!vis[j]) pq.push((node){j,d[j]});
}
}
}
}
void build(){
int a,b,c;
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++)
scanf("%d",&a),locked[a]=true;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a,&b,&c),add(a,b,c);
scanf("%d%d",&x,&y);
}
void solve(){
memset(d1,0x3f,sizeof(d1));
memset(d2,0x3f,sizeof(d2));
memset(d3,0x3f,sizeof(d3));
dijstra(1,d1,true);
dijstra(1,d2,false);
dijstra(x,d3,true);
int ans=0x7fffffff;
//情况一:哈利罗恩分别去一间密室
ans=min(max(d1[x],d2[y]),max(d1[y],d2[x]));
//情况二:哈利一个人去两间密室
ans=min(ans,min(d1[x],d1[y])+d3[y]-d3[x]);
printf("%d\n",ans);
}
int main(){
build();
solve();
return 0;
}
T3:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct P{
int x,y;
};
queue<P>body[25];//储存蛇身
struct snake{//储存分数,id
int id,len;
bool operator < (const snake& B){
return len>B.len||(len==B.len&&id<B.id);
}
}s[25];
int dx[]={1,0,0,-1};
int dy[]={0,-1,1,0};
int n,m,k,x,id;
char a[205][205],cmd[25][105];
int mp[205][205],len[25],head[25][2];//mp[i][j]==-1表示食物
bool not_in(int i,int j){
if(i<1||j<1||i>n||j>m) return true;
return false;
}
void print(){//仅用于查看中间结果
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
if(mp[i][j]>0) printf("%d",mp[i][j]);
else putchar(mp[i][j]?'&':'.');
putchar('\n');
}
putchar('\n');
}
void dfs(int i,int j,int id_){//四连块搜索蛇身
if(!mp[i][j]&&(a[i][j]=='#'||(a[i][j]=='@'&&len[id_]==0)))
mp[i][j]=id_,len[id_]++;
else return;
for(int ii=0;ii<4;ii++)
dfs(i+dx[ii],j+dy[ii],id_);
body[id_].push((P){i,j});//队列维护贪吃蛇蛇身
}
void die(int id_){//贪吃蛇死亡
len[id_]=0;
while(!body[id_].empty()){
P T=body[id_].front();
body[id_].pop();
mp[T.x][T.y]=-1;//变成食物
}
}
void move(int i,int j,int id_,int case_){//贪吃蛇移动
int i_=i+dx[case_],j_=j+dy[case_];
if(mp[i_][j_]>0||not_in(i_,j_)) die(id_);//撞到边界或蛇导致死亡
else if(mp[i_][j_]==-1){//吃到食物
len[id_]++,mp[i_][j_]=id_;
head[id_][0]=i_,head[id_][1]=j_;
body[id_].push((P){i_,j_});
}
else{//移动一步
mp[i_][j_]=id_,head[id_][0]=i_,head[id_][1]=j_;
body[id_].push((P){i_,j_});
P T=body[id_].front();
body[id_].pop();
mp[T.x][T.y]=0;
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(a[i][j]=='@')//放心大胆的四连块搜索
dfs(i,j,++id),head[id][0]=i,head[id][1]=j;
else if(a[i][j]=='&')
mp[i][j]=-1;
for(int i=1;i<=id;i++) scanf("%s",cmd[i]);
for(int i=0;i<k;i++){
for(int i_=1;i_<=id;i_++) if(len[i_]>0){//如果蛇没有死才移动
switch(cmd[i_][i]){//注意操作与dx[],dy[]的对应关系
case 'W':move(head[i_][0],head[i_][1],i_,3);break;
case 'A':move(head[i_][0],head[i_][1],i_,1);break;
case 'S':move(head[i_][0],head[i_][1],i_,0);break;
case 'D':move(head[i_][0],head[i_][1],i_,2);break;
}
}
// print(); /*查看中间结果*/
}
for(int i=1;i<=id;i++)
s[i].id=i,s[i].len=len[i];
sort(s+1,s+id+1);
for(int i=1;i<=id;i++) printf("%d %d\n",s[i].len,s[i].id);
int cnt=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(mp[i][j]==-1) cnt++;
printf("%d",cnt);
return 0;
}
总结:答疑好累呀(-_-||),我现在头晕眼花(-_-||),看了一下别人举办的比赛,大部分难度都比较大,如果我要举办模拟赛DAY2,那么一定会加大难度。