首页 > 代码库 > NOIP2014提高组总结

NOIP2014提高组总结

NOIP2014提高组总结

-by mps

 尽管今年没参加NOIP2014提高组,但是做了一下,还是有感受的,在这里写出我500分的思路(满分以后会更改,毕竟能力有限......)

 Day 1

 T1 生活大爆炸版石头剪子布

 【题目大意】

   石头剪子布大家都玩过,只不过这题加了“斯波克”和“蜥蜴人”,事实上还是蛮简单的,有基本逻辑推理常识和基本代码处理能力即可AC,放在PJ都是第一题的难度...

   一般有三种做法:

    文艺青年:写个矩阵来表示得失,注意要判断两次(甲对乙及乙对甲)

    普通青年:16个if嵌套

    二B青年:25个if无嵌套

    100分到手

  代码:

 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6  7 const int MaxN=201; 8 const int f[5][5]={0,0,1,1,0, 9                    1,0,0,1,0,10                    0,1,0,0,1,11                    0,0,1,0,1,12                    1,1,0,0,0};13 int a[MaxN],b[MaxN];14 int na,nb,n;15 16 void readfile(){17   freopen("rps.in","r",stdin);18   freopen("rps.out","w",stdout);19   scanf("%d%d%d",&n,&na,&nb);20   int i;21   for(i=1;i<=na;i++)22     scanf("%d",&a[i]);23   for(;i<=n;i++)a[i]=a[i-na];24   for(i=1;i<=nb;i++)25     scanf("%d",&b[i]);26   for(;i<=n;i++)b[i]=b[i-nb];27 }28 29 void solve(){30     int i,sa=0,sb=0;31     for(i=1;i<=n;i++){32         if(f[a[i]][b[i]]==1)sa++;33         else if(f[b[i]][a[i]]==1)sb++;34     }35     printf("%d %d",sa,sb);36 }37 38 int main(){39   readfile();40   solve();41   return 0;42 }

 

 T2 联合权值

 【题目大意】

   给你一张图,用无向边来联通这个图,每一条边的长度均为1,求距离为2的点的权值乘积的最大值和总和

   难点来了:对于30%的数据 1<n≤100

                  对于50%的数据 1<n≤2000

                  对于100%的数据 1<n≤200000

   拿到题目有一种很明显的算法:

      求出每对顶点的距离,然后O(N^2)枚举,求和找最大值

      具体实现就是floyed,O(N^3+N^2),30分

    我们发现没有必要去求每一对顶点的距离,所以任选一点作为起点,bfs一遍,然后两个点的距离就可以用类似前缀和的形式求出

    时间复杂度O(N^2),50分

    事实上题目并没有这么难,我们进行数学分析

           题目的意思很明显就是求一个点的前驱和后继的乘积和以及最大值

             我们设x表示中间的点,它的前驱是a,b,c,后继是d,e,f

             这里有一点要注意,并不是a只能和def配对,还可以和b,c配对

             那么怎么求呢?

             正好初二,学了整式的乘法

            (1) (ab+ac+ad+ae+af)+(ba+bc+bd+be+bf)+(ca+cb+cd+ce+cf)+(da+db+dc+de+df)+(ea+eb+ec+ed+ef)+(fa+fb+fc+fd+fe)

            (2) (a+b+c+d+e+f)^2

            (2)-(1),得

 =-(2ab+2ac+2bc+2ad+2ae+2af+2bd+2be+2bf+2cd+2ce+2cf+2de+2df+2ef-aa-2ab-2ac-2ad-2ae-2af-bb-2bc-2bd-2be-2bf-2cd-2ce-2cf-...-2ef)

     =aa+bb+cc+dd+ee+ff

            所以我们还需要填补每一项的平方

            那么对于一个点,通过它能产生的联合权值=(∑前驱+∑后继)^2-{∑[(前驱后继的每一个)^2]}

            如何计算最大值?更简单!只要弄两个域来控制即可

          显然我们用中间结构体来存储。

       100分到手!

       代码:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int MaxN=200001;const int Mod=10007;struct READ{//离线处理    int u,v;}data[MaxN];struct element{    int pri_sum,ne_sum;    int w;    int rec;    int m1,m2;    element(){rec=m1=m2=pri_sum=ne_sum=0;}}a[MaxN];int n;int ans,MAX;void readfile(){  freopen("link.in","r",stdin);  freopen("link.out","w",stdout);  scanf("%d",&n);  int i,u,v;  for(i=1;i<n;i++)    scanf("%d %d",&data[i].u,&data[i].v);  for(i=1;i<=n;i++)    scanf("%d",&a[i].w);  for(i=1;i<n;i++){      a[data[i].u].ne_sum=(a[data[i].u].ne_sum+a[data[i].v].w)%Mod;       a[data[i].v].pri_sum=(a[data[i].v].pri_sum+a[data[i].u].w)%Mod;      a[data[i].u].rec=(a[data[i].u].rec+a[data[i].v].w*a[data[i].v].w)%Mod;      a[data[i].v].rec=(a[data[i].v].rec+a[data[i].u].w*a[data[i].u].w)%Mod;      if(a[data[i].u].w>a[data[i].v].m1){a[data[i].v].m2=a[data[i].v].m1;a[data[i].v].m1=a[data[i].u].w;}      else if(a[data[i].u].w>a[data[i].v].m2)a[data[i].v].m2=a[data[i].u].w;      if(a[data[i].v].w>a[data[i].u].m1){a[data[i].u].m2=a[data[i].u].m1;a[data[i].u].m1=a[data[i].v].w;}      else if(a[data[i].v].w>a[data[i].u].m2)a[data[i].u].m2=a[data[i].v].w;  }}void solve(){  int i,j,k;  for(i=1;i<=n;i++){      MAX=max(MAX,(a[i].m1*a[i].m2));      ans=(ans+(a[i].ne_sum+a[i].pri_sum)*(a[i].ne_sum+a[i].pri_sum)-a[i].rec)%Mod;  }  printf("%d %d",MAX,ans); }int main(){  readfile();  solve();  return 0;}

 

T3 飞扬的小鸟

话说基本上每年都有一道游戏题→_→

【题目描述】

  Flappy Bird 是一款风靡一时的休闲手机游戏。玩家
需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让
小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了
水管或者掉在地上的话,便宣告失败。
  为了简化问题,我们对游戏规则进行了简化和改编:
  1. 游戏界面是一个长为n,高 为m的二维平面,其中有
k个管道(忽略管道的宽度)。 
  2. 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边
任意整数高度位置出发,到达游戏界面最右边时,游
戏完成。
  3. 小鸟每个单位时间沿横坐标方向右移的距离为1,竖直移动的距离由玩家控制。如
果点击屏幕,小鸟就会上升一定高度X,每个单位时间可以点击多次,效果叠加;
如果不点击屏幕,小鸟就会下降一定高度Y。小鸟位于横坐标方向不同位置时,上
升的高度X和下降的高度Y可能互不相同。
  4. 小鸟高度等于0或者小鸟碰到管道时,游 戏 失 败 。小 鸟 高 度 为m时,无法再上升。
现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟
最多可以通过多少个管道缝隙。

 

拿到题目仔细阅读后可以发现DP做法

我们设f(i,j)表示鸟飞到横坐标为i,纵坐标为j的位置时所需要的最少触屏次数

考虑了前后决策,我们发现不可能由前一阶段推到当前阶段,因为题目说:“遇到顶部不再上升”,所以我们可以用本阶段考虑下一阶段。

                     f(i,j-down[i])               (j-down[i]>0)            

f(i+1,j)=min{  f(i,j+up[i]*k)+k           (j+up[i]*k<=m,1<=k)                     {0<=i<n 1<=j<=m}

                     f(i,m)+k                      (j+up[i]*k>m)

时间复杂度O(N*M^2),70分到手

本蒟蒻只会这种70分算法,希望大牛能够在评论区教导100分算法,感激不尽

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int MaxN=10005;const int MaxM=1005;int f[MaxN][MaxM];//f(i,j)表示小鸟飞到(i,j)这个位置所需的最少触屏次数int n,m,PIPE;bool board[MaxN][MaxM];int up[MaxN],down[MaxN];int x[MaxN];void init(){    freopen("bird.in","r",stdin);    freopen("bird.out","w",stdout);    scanf("%d%d%d",&n,&m,&PIPE);    int i,upx,downx,j;    for(i=0;i<n;i++)      scanf("%d%d",&up[i],&down[i]);    memset(board,false,sizeof(board));    for(i=1;i<=PIPE;i++)    {        scanf("%d%d%d",&x[i],&upx,&downx);        for(j=0;j<=upx;j++)          board[x[i]][j]=true;        for(j=downx;j<=m;j++)          board[x[i]][j]=true;    }}int minx;void ok(){printf("1\n%d",minx);}void no(){    int pas=0,i,j;    for(i=n-1;i>=0;i--)    {       for(j=0;j<=m;j++)        if(f[i][j]!=0x3f3f3f3f)break;       if(j<=m)       {         for(j=1;j<=PIPE;j++)           if (x[j]<=i)             pas++;         printf("0\n%d",pas);         return;       }       pas=0;    }}/*动态转移方程:  f(i,j)=min{f(i-1,j+down[i-1]),min{f(i-1,j-up[i-1]*k)}}   (1<=i<=n,0<=j<=m,j-up[i-1]*k>=0)  特别地,f(0,i)=0  {0<=i<=m}*/ void dp(){    int i,j,k,l,sumw=0;    memset(f,0x3f3f3f3f,sizeof(f));    for(i=0;i<=m;i++)f[0][i]=0;    for(i=0;i<n;i++)      for(j=1;j<=m;j++)      if(board[i][j]==false)      {           if(j-down[i]>0 && board[i+1][j-down[i]]==false)           f[i+1][j-down[i]]=min(f[i+1][j-down[i]],f[i][j]);           for(k=1;k<=m;k++)            if(j+up[i]*k<=m && board[i+1][j+up[i]*k]==false)              f[i+1][j+up[i]*k]=min(f[i][j]+k,f[i+1][j+up[i]*k]);            else               if(j+up[i]*k>m && board[i+1][m]==false)//上升至顶部不再上升                 {                  f[i+1][m]=min(f[i+1][m],f[i][j]+k);                  break;                }      }    minx=0x3f3f3f3f;    for(i=1;i<=m;i++)      minx=min(minx,f[n][i]);    if(minx==0x3f3f3f3f)no();    else ok();}int main(){    init();    dp();    return 0;} 

 

Day 1总结

100/100/70分到手,其实题目很水,不过如果去考场考的话,估计会严重缩水

 

Day 2

T1 无线网路发射器设置

【题目描述】

  在[0,128][0,128]的矩阵中找到一个位置,使得以它为中心所产生的边长为2*d的正方形能够覆盖最多公共区域

很简单的一道题目,朴素枚举然后O(N)扫描一趟,由于N≤20,所以128*128*20也是无压力水过

还记得有位大牛当场码了一个树状数组套树状数组求和,然后速度飞快,不过有必要么?

100分到手

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int MaxN=21;const int MaxS=128;int x[MaxN],y[MaxN],k[MaxN];int n,d;void readfile(){  freopen("wireless.in","r",stdin);  freopen("wireless.out","w",stdout);  scanf("%d%d",&d,&n);  for(int i=1;i<=n;i++)      scanf("%d%d%d",&x[i],&y[i],&k[i]);}void solve(){    int i,j,kk,res=0,num=0,calc=0;    for(i=0;i<=MaxS;i++)      for(j=0;j<=MaxS;j++){          calc=0;          for(kk=1;kk<=n;kk++)            if(i-d<=x[kk] && j-d<=y[kk] && j+d>=y[kk] && i+d>=x[kk])              calc+=k[kk];          if(calc>res){              res=calc;              num=1;          }        else if(calc==res)                num++;      }    printf("%d %d",num,res);}int main(){  readfile();  solve();  return 0;}

T2 寻找道路

【题目描述】

  在有向图G中,每条边的长度均为1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:

1.路径上的所有点的出边所指向的点都直接或间接与终点连通。
2.在满足条件1的情况下使路径最短。
注意:图G中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度

 

没什么难的,水2次SPFA,第一次在反向图中跑,只要到达某个点的次数不等于其在正向图中的出度,就标记为1,否则为0

第二次直接一遍SPFA求单对定点最短路,切记不可走标记为1的点

无压力AC

100分到手

#include <iostream>using namespace std;#include <cstdio>#include <deque>#include <queue>#include <cstring>const int MaxN=10001;const int L=1500;struct point{    int v[L];    int len;    point(){len=0;}}g[MaxN],f[MaxN];int dis[MaxN];int flag[MaxN];int n,m,s,t;void init(){     freopen("road.in","r",stdin);     freopen("road.out","w",stdout);     scanf("%d%d",&n,&m);     int i,u,v;     for(i=1;i<=m;i++){        scanf("%d %d",&u,&v);        g[u].v[++g[u].len]=v;        f[v].v[++f[v].len]=u;     }     scanf("%d%d",&s,&t);}deque<int> q;void spfa(int u){     int head,i;     memset(dis,0x3f3f3f3f,sizeof(dis));     dis[u]=0;     q.push_front(u);     while(!q.empty()){       head=q.front();       q.pop_front();         for(i=1;i<=g[head].len;i++){         if(dis[head]+1<dis[g[head].v[i]] && flag[g[head].v[i]]==1){           dis[g[head].v[i]]=dis[head]+1;           if(dis[head]>=dis[g[head].v[i]])q.push_front(g[head].v[i]);             else q.push_back(g[head].v[i]);         }       }     }}deque<int> Q;int cnt[MaxN];void spfa2(int u){     int head,i;     memset(dis,0x3f3f3f3f,sizeof(dis));     dis[u]=0;     Q.push_front(u);     while(!Q.empty()){       head=Q.front();       Q.pop_front();         for(i=1;i<=f[head].len;i++){            cnt[f[head].v[i]]++;         if(dis[head]+1<dis[f[head].v[i]]){           dis[f[head].v[i]]=dis[head]+1;           if(dis[head]>=dis[f[head].v[i]])Q.push_front(f[head].v[i]);             else Q.push_back(f[head].v[i]);         }       }     }     for(i=1;i<=n;i++)       if(cnt[i]==g[i].len || i==t)flag[i]=1;}void solve(){    memset(flag,0,sizeof(flag));    int i;    spfa2(t);    if(!flag[s]){cout<<-1;return;}    spfa(s);    cout<<dis[t];}int main(){    init();    solve();    return 0;}

 

T3 解方程

【题目大意】

  对于一个方程

   a0+a1*x+a2*x^2+a3*x^3+...+an*x^n=0

  求其在[1,m]的解的个数以及所有解

 数据范太恐怖了,不想写→_→

 首先Orz @LZZ大神高一AK 提高组,而且能做出这题实在是NB

 作为蒟蒻的我只能拿30分,如果不嫌烦的话可以试试70分,高精度+压位

 听某神牛说,只要模一个大质数然后求模意义下的解即可,居然还有70分,我也是醉了

  100分听LZZ大神说是FFT+秦九韶

代码就不水了,话说拿去问数学老师,老师果断回绝了我→_→

 

Day 2总结

100/100/30  比day1差了一些,主要有T3在,= =,数学不过关啊

 

NOIP2014提高组总结

初二这个年级不适合参加提高组,所以就去参加PJ了,还给坑了→_→,下次不和老师玩了。。

今年可能是由于面向社会征题的原因,异常的水,安徽的分数线是375分,我500分,顺利1=(真后悔听老师的)

其实每一天的前两题都不难,主要靠基础能力,而第三题则涉及一些诡异算法,还好飞扬的小鸟的DP方程很好想,又拿了100分

只要做出每天前两题,其余题目能骗分就骗分,能暴力就暴力,1等是没有问题的

 

NOIP2014提高组总结