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

版权声明:欢迎转载+原文章地址~ https://blog.csdn.net/Hi_KER/article/details/82782098

前言:

这场比赛的题是大约一个月前出的,当时刚从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<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,那么一定会加大难度。

猜你喜欢

转载自blog.csdn.net/Hi_KER/article/details/82782098