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在,= =,数学不过关啊

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

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

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

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

时间: 2024-12-27 21:43:17

NOIP2014提高组总结的相关文章

[NOIP2014] 提高组 洛谷P2038 无线网络发射器选址

题目描述 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平行的129 条东西向街道和129 条南北向街道所形成的网格状,并且相邻的平行街道之间的距离都是恒定值 1 .东西向街道从北到南依次编号为0,1,2…128 , 南北向街道从西到东依次编号为0,1,2…128 . 东西向街道和南北向街道相交形成路口,规定编号为x 的南北向街道和编号为y 的东西向街道形成的路口的坐标是(x , y ). 在 某 些 路口存在一定数量的公共

【学术篇】luogu1351 [NOIP2014提高组] 联合权值

一道提高组的题..... 传送门:题目在这里.... 现在都懒得更自己的blog了,怕是太颓废了_ (:з」∠) _ 好久没做题了,手都生了.(好吧其实是做题方面手太生了) 这题我都不想讲了,把代码一贴就算了呗.. 但还是要说说的.... 首先,题目里说:"无向连通图G 有n 个点,n - 1 条边." 我们可以知道这是一棵树(怕不是废话..),这样遍历的时候就能保证是O(n)级别了.. 找最大值 很简单,遍历树的时候找一下与每个点相连的点的最大值和次大值一乘就完了...显然这么贪心是

[NOIP2014] 提高组 洛谷P2296 寻找道路

题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点连通. 2 .在满足条件1 的情况下使路径最短. 注意:图G 中可能存在重边和自环,题目保证终点没有出边. 请你输出符合条件的路径的长度. 输入输出格式 输入格式: 输入文件名为road .in. 第一行有两个用一个空格隔开的整数n 和m ,表示图有n 个点和m 条边. 接下来的m 行每行2 个整数x .y ,之间用一个

[NOIP2014] 提高组 洛谷P1941 飞扬的小鸟

题目描述 Flappy Bird 是一款风靡一时的休闲手机游戏.玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙.如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败. 为了简化问题,我们对游戏规则进行了简化和改编: 游戏界面是一个长为n ,高为 m 的二维平面,其中有k 个管道(忽略管道的宽度). 小鸟始终在游戏界面内移动.小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成. 小鸟每个单位时间沿横坐标方向右移的距离为1 ,竖直移动

[NOIP2014] 提高组 洛谷P1351 联合权值

题目描述 无向连通图G 有n 个点,n - 1 条边.点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 .图上两点( u , v ) 的距离定义为u 点到v 点的最短距离.对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu ×Wv 的联合权值. 请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少? 输入输出格式 输入格式: 输入文件名为link .in. 第一行包含1 个整数n . 接下来n - 1 行,

NOIP2014提高组解方程

其实没有太难 但是不知道的话想不到 考场上大概有50分吧 1 #include <iostream> 2 #include <stdio.h> 3 #include <queue> 4 5 using namespace std; 6 7 int mod[7 + 2] = { 19 , 101 , 11261 , 19997 , 22877 , 21893 , 14843 }; 8 int n , m , ans[1000000 + 2]; 9 char a[100 +

[NOIP2014提高组]解方程

题目:BZOJ3751.洛谷P2312.UOJ#20.Vijos P1910.codevs3732. 题目大意:已知多项式方程: 求这个方程在[1, m]内的整数解(n 和 m 均为正整数). 解题思路:因为$0=0$(废话),能得出$0+x·p\equiv 0(mod\ p)$. 也就是当方程右边为0时,方程左边mod p为0. 但方程左边mod p等于0时,方程右边不一定等于0. 但是也不一定不等于0. 所以我们如果多引入几个p(最好是素数),对其进行测试,发现都为0的话,那我们就可以认为它

[NOIP2014] 提高组 洛谷P2312 解方程

题目描述 已知多项式方程: a0+a1x+a2x^2+..+anx^n=0 求这个方程在[1, m ] 内的整数解(n 和m 均为正整数) 输入输出格式 输入格式: 输入文件名为equation .in. 输入共n + 2 行. 第一行包含2 个整数n .m ,每两个整数之间用一个空格隔开. 接下来的n+1 行每行包含一个整数,依次为a0,a1,a2..an 输出格式: 输出文件名为equation .out . 第一行输出方程在[1, m ] 内的整数解的个数. 接下来每行一个整数,按照从小到

Noip2014 提高组 day1 T1&#183; 生活大爆炸版石头剪刀布

生活大爆炸版 石头剪刀布 描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一 样,则不分胜负.在<生活大爆炸>第二季第 8 集中出现了一种石头剪刀布的升级版游戏. 升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势: 斯波克:<星际迷航>主角之一. 蜥蜴人:<星际迷航>中的反面角色. 这五种手势的胜负关系如表一所示,表中列出的是甲对乙的游戏结果. 现在,小 A 和小 B 尝试玩这种升级版的猜拳游戏.已知他们的出拳都是有周期性规律的