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等是没有问题的

 

posted @ 2014-12-19 00:26  MedalPluS  阅读(514)  评论(0编辑  收藏  举报