【洛谷】NOIP2018原创模拟赛DAY1题解

前言:

这场比赛的题是大约一个月前出的,当时刚从NOIP2001-NOIP2011之间的题目比较顺利地走过来。当时感觉NOIP并不是很难,但在这一个月经历了很多,最近几年的NOIP瞬间让我意识到自己能力的不足,特别是在NOIP2012,NOIP2015中惨败,又见识了起步早,能力强的小学/初中大佬,这让学了15个月OI马上要参加最后一次NOIP的我倍感压力。

从近年NOIP的考试趋势来看,部分考察知识开始涉及省选或以前不常考内容,题目难度也有所上升。现在看这套题,我觉得题目风格偏旧,难度不足,有点像NOIP2011之前的考试风格。总之,有诸多不足,望见谅。如果有误,请指出。

比赛链接:https://www.luogu.org/contestnew/show/10364

数据下载:https://share.weiyun.com/5HFfVLB

密码:noip    (注意:T1#10,T2#4,T3#4有bug)

T1:小凯的数字

注:关于下面引理的证明,我第一次了解是在某数学竞赛黑皮书上

考察知识:数学,数论,模拟

算法难度:XX+ 实现难度:X

评价:这还是算一道和NOIP切和很好的题目,数学类题目是近年NOIP必考题。

感想:我本来以为然而

分析:

这道题用类似高精度除法的方法可以做,但是肯定要超时。

正解和NOIP2017T1解法有点相似,也是推(cai)结(da)论(an)的题

结论:对于输入的l,r输出:\frac{(l+r)(r-l+1)}{2}\,\,mod\,\,9

坑点:如果不先取模再乘会爆 long long

证明:

先证明一个引理:对于数 \overline{a_{n-1}a_{n-2}...a_2a_1a_0}\,\,\,(0\leqslant a_i\leqslant 9,a_i\in N) 

        有:\sum^{n-1}_{i=0}a_i\equiv \overline{a_{n-1}a_{n-2}...a_2a_1a_0}\,\,(mod \,\,9)

注意到:10\equiv 1\,(mod \,\,\,9) 则  10^k\equiv 1(mod \,\,9) \,\,\,(k\in N,k\geqslant 0) ,

那么:\overline{a_{n-1}a_{n-2}...a_2a_1a_0}=\sum_{i=0}^{n-1}10^ia_i\equiv \sum_{i=0}^{n-1}a_i (mod\,\,9)

引理得证。

我们已经离结论很近了,如果我们单纯把 [l,r] 的数字进行分解还是要超时,所以我们还要继续优化:

同理我们可以证明: \overline{a_{n-1}a_{n-2}...a_2a_1a_0}\equiv \sum_{i=0}^{n-1}a_i(mod\,\,9) \,\,\,(a_i\geqslant 0,a_i\in N)

即:\overline{l(l+1)...(r-1)r}\equiv \sum_{i=l}^{r}i\,(mod\,\,9) \,\,\,(l> 0)

由等差数列求和公式:\sum_{i=l}^ri=\frac{(l+r)(r-l+1)}{2}

结论得证。

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
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
#include
#include
#include
#include
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_queuepq;
    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

T3:

#include
#include
#include
#include
#include
using namespace std;
struct P{
    int x,y;
};
queue

body[25];//储存蛇身 struct snake{//储存分数,id int id,len; bool operator < (const snake& B){ return len>B.len||(len==B.len&&idn||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;i0){//如果蛇没有死才移动 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,那么一定会加大难度。

 

你可能感兴趣的:(竞赛考试)