2017清北学堂(提升组精英班)集训笔记——备战指导与往年经典例题

1、比赛策略:html

1.比赛成绩=比赛经验×自身实力。ios

2.比赛前在不断提升本身实力的同时,积累大量经验。git

3.千万不要copy代码,不过能够抄代码,看到优秀的代码,边抄边想这个是怎么来的,抄多了天然就会熟练,最好抄到你打键盘都手软!算法

4.平时比赛多写写对拍,比赛时候就写得出了,能大幅提升正确率。数组

5.正式比赛的时候最主要的就是细心,拿能拿到的分数。大数据

6.要有自信->我不拿第一谁拿第一??优化

2、乱入两个钟神的Tips:网站

①咱们在读入long long类型数据时,要根据评测程序的操做系统来选择是"%I64d"仍是"%lld",可是很麻烦啊,有没有什么办法不用这样搞?spa

确定有啊!只要在程序头文件下方加上这些代码便可!操作系统

 1 #include <stdio.h>
 2 #ifdef WIN32//如下自动判断当前系统! 
 3 #define LL "%I64d"
 4 #else
 5 #define LL "%lld"
 6 #endif
 7 int main()
 8 {
 9     int x=100;
10     long long gg=200;
11     printf("%d " LL,x,gg);
12     return 0;
13 }
14 /*
15 output:
16 100 200
17 */

②C++中还有long double这个类型的数,但不能读入和输出,只能在计算过程当中使用!

3、往年经典例题:

经典例题:借教室(Luogu 1083 NOIP2012 D2T2)

  其中第i天学校有ri个教室可供租借。

  共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者须要从第sj天到第tj天租借教室(包括第sj天和第tj天),天天须要租借dj个教室。

  即对于每份订单,咱们只须要天天提供dj个教室,而它们具体是哪些教室,天天是不是相同的教室则不用考虑。

  借教室的原则是先到先得,也就是说咱们要按照订单的前后顺序依次为每份订单分配教室。若是在分配的过程当中遇到一份订单没法彻底知足,则须要中止教室的分配,通知当前申请人修改订单。这里的没法知足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。

  求至少到第几个订单没法知足条件。

  数据范围:1≤n,m≤106,0≤ri,dj≤109,1≤sj≤tj≤n。

算法描述

这个题实际上就是让咱们维护区间减法和求区间最小值操做

作法一:线段树(70分)

两个操做:①将[dj,sj]的值同时减小tj;②查询最小值是否<0。

作法二:二分订单数(100分)

假如前x个订单能知足要求,那么前x-1个订单也必定能知足要求;

假如前x个订单不能知足要求,那么前x+1个订单也必定不能知足要求。

二分答案mid→判断前mid个订单可否知足要求,若是能够,答案≥mid,不然答案<mid。

操做:s[dj]=s[dj]+tj;s[sj+1]=s[sj+1]-tj

例如:一开始数列s[]是0 0 0 0 0 0

3到5天须要2的教室,那么将s[3]+=2,s[5+1]-=2。

数列s[]变为:0 0 2 0 0 -2

前缀和数组为:0 0 2 2 2 0,这样就实现了增长3~5天须要的教室数,接下来二分订单数,处理前mid个订单看看是否有某一天的前缀和大于di。

 1 #include<iostream>//时间复杂度:O(n+m)
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int n,m,ans,a[1000010],d[1000010],x[1000010],y[1000010],s[1000010],sum;
 6 template <class T> T get(T &u)//读入优化 
 7 {
 8     char x;
 9     for(;!isdigit(x=getchar()););
10     for(u=x-48;isdigit(x=getchar());u*=10,u+=(x-48));
11     ungetc(x,stdin);
12     return u;
13 }
14 bool judge(int v)
15 {
16     memset(s,0,sizeof(s));
17     sum=0;//初始化前缀和 
18     for(int i=1;i<=v;i++)//使人窒息的操做 
19     {
20         s[x[i]]+=d[i];
21         s[y[i]+1]-=d[i];
22     }
23     for(int i=1;i<=n;i++)//sum前缀和操做 
24     {
25        sum+=s[i];
26        if(sum>a[i]) return 0;//有某一天的前缀和大于di,返回0 
27     }
28     return 1;//没有,返回1
29 }
30 int main()
31 {
32     get(n),get(m);//读入n,m 
33     for(int i=1;i<=n;i++) get(a[i]);//读入原数据数组a[] 
34     for(int i=1;i<=m;i++)//读入信息 
35         get(d[i]),get(x[i]),get(y[i]);
36     int l=1,r=m;//二分初始化,左为1,右为订单数 
37     while(l<=r)
38     {
39         int mid=(l+r)>>1;
40         if(!judge(mid))
41         {
42             ans=mid;
43             r=mid-1;
44         }
45         else l=mid+1;
46     }
47     if(!ans) printf("0");
48     else printf("-1\n%d",ans);
49     return 0;
50 }

代码来自:http://hzwer.com/2959.html

经典例题:乌龟棋(Luogu 1541 NOIP2010 D2T2)

  乌龟棋的棋盘是一行 N 个格子,每一个格子上一个分数(非负整数)。棋盘第 1 格是惟一的起点,第 N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
  乌龟棋中 M 张爬行卡片,分红 4 种不一样的类型( M 张卡片中不必定包含全部 4 种类型的卡片,见样例),每种类型的卡片上分别标有 一、 二、 三、4 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次须要从全部的爬行卡片中选择一张以前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。
  游戏中,乌龟棋子自动得到起点格子的分数,而且在后续的爬行中每到达一个格子,就获得该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程当中到过的全部格子的分数总和。

- 棋盘: 6 10 14 2 8 8 18 5 17
- 卡片: 1 3 1 2 1
- 答案: 73 = 6+10+14+8+18+17

  数据范围:1≤N≤350,1≤M≤120

算法描述
很明显,用不一样的爬行卡片使用顺序会使得最终游戏的得分不一样,小明想要找到一种卡片使用顺序使得最终游戏得分最多。如今,告诉你棋盘上每一个格子的分数和全部的爬行卡片,你能告诉小明,他最多能获得多少分吗?

* 思考:如何进行搜索?状态该如何设计?
* DFS(x,c1,c2,c3,c4) 为当前在第 x 个格子上, ci 表明标有数字i 的卡片有多少张。
* 因而能够直接写出状态 F[i][a][b][c][d],与状态是一一对应的,表示该状态下, 最大的权值是多少(当前第i个格子,四种牌分别用了i,j,k,l张的状况下,能达到的最优值)。
* 因而,能够和 F[i-1],F[i-2]…进行联系

 1 //T27:乌龟棋(我把i省略了,其实没什么用,加上只是用来判断是否越界而已)
 2 for(int i=0;i<=a;i++)
 3        for(int j=0;j<=b;j++)
 4           for(int k=0;k<=c;k++)
 5              for(int l=0;l<=d;l++)
 6                 {
 7                     if(i!=0)f[i][j][k][l]=max(f[i][j][k][l],f[i-1][j][k][l]);
 8                     if(j!=0)f[i][j][k][l]=max(f[i][j][k][l],f[i][j-1][k][l]);
 9                     if(k!=0)f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k-1][l]);
10                     if(l!=0)f[i][j][k][l]=max(f[i][j][k][l],f[i][j][k][l-1]);
11                     f[i][j][k][l]+=s[i+j*2+k*3+l*4];
12                 }

经典例题:运输计划(Luogu 2680 NOIP2015 D2T3)

  问题简化:给定一棵有n个节点的带边权的树,以及m个条件ai,bi。能够将某一条边的边权变为0,目标使max{dis(ai,bi)}最小(全部路径费用的的最大值最小)。

  数据范围:n,m≤300000。

算法描述

咱们能够换个角度思考:枚举答案x而不是删除某条边,答案显然能够二分!

对于全部长度超过x的条件,在树中将这些边标记,表示这些边中必须删除一条边,标记方法表示为:f[ai]++,f[bi]++,f[lca(ai,bi)]=f[lca(ai,bi)]-2;

最终被全部长度超过x的条件标记的边中找出最长的边,判断是否可行

NOIP的时候,遇到一道题目是树剖→你想错了!

题目中要求“最大值最小”或“最小值最大”,95%是二分答案!

假如存在一种方案最大值不会超过x,那么一定存在一种方案最大值不会超过x+1。

假如不存在方案使得最大值不超过x,那么一定不存在方案使得最大值不超过x-1。

x知足连续性。

咱们二分答案获得一个值mid(在最长链和最短链长度中二分出mid),判断是否存在一种方案(把一条边的边权变为0),使得全部链的长度都≤mid

若是说对于某一条链,它一开始的长度≤mid,合理!一开始的长度>mid,那么这一条链绝对存在一条边的边权变为0,执行m条便可。

这样,问题转化成:可否删除一条边,使得这m条链的链长长度都≤mid。

对于一条链,在树上标记下来,表示这些边必须得有一条被删掉,每条链都这么作。

若是一条边,被全部链都标记过,说明删掉这条边后可能能知足要求,假如说存在多条这样的边,确定把最长的边的边权变为0,最后判断全部链是否都知足条件。

假设最长的链长度为=x,最长的知足条件的边的边权=y,只有当x-y≤mid时,知足条件。

标记方法

对于一条链,在树上标记下来:一条链能够被分红两条链,其中每条链都知足两个端点u、v知足u是v的祖先,即:f[v]++,f[u]--;最后求以每一个点为根的子树中f的和,就是它被标记的次数!

时间复杂度:O(nlgn)。

经典例题:华容道(Luogu 1979 NOIP2013 D2T3)

有一张n×m的图,其中有一些是障碍。

有q个询问,每一个询问给定空白格子,初始格子,目标格子。

每次操做只能将空白格子与其相邻的非障碍格子交换位置,对于每次询问,输出要使初始格子到达目标格子的最小步数。

n,m<=30,q<=500;n,m<=30,q<=10(70分)。

解题过程

一个比较暴力的作法:

对于每组询问,直接广搜BFS,令f[i][j][k][l]表示此时空白格子在(i,j),初始格子在(k,l)时的最小步数,每次至关于空白格子在走,转移比较简单。

时间复杂度为q×n2×m2

另外一个优化版的作法:

考虑初始格子移动的本质

每当初始格子移动后,空白格子必定在它周围

所以咱们能够预处理出这么一个东西。

令dp[i][j][0/1/2/3][0/1/2/3]表示初始格子在(i,j),空白格子在其相邻的哪一个方向,下一步该初始格子要走到相邻的哪一个方向的最小步数。

其中每次询问都要先将空白格子移动到初始格子周围

时间复杂度为n2×m2+shortest_path(n×m,n×m)×q。

经典例题:打怪兽(野题)

有n<=10W只怪兽,你能够随便选个顺序打过去,初始你有a滴血,打掉第i个怪兽会扣掉你bi滴血,扣完后会掉落一个血瓶,你能够回复ci点血,打完这n个怪兽后你才能去见BOSS。

当你血量<=0时你会挂掉。

问你可否见到BOSS,输出任意一种可行的方案。

解题思路

按照通常的贪心思路,首先想到性价比什么的。。。可是发现这些都不对。。。

一种正确的贪心思路:“先吃饱再消化”,意思就是首先把怪兽分为两种,一种能给你加血(ci-bi≥0),另外一种会扣你血(ci-bi<0)。

这样就变成了两个简单的问题:

加血的咱们应该怎么打?——按伤害从小到大排序

会掉血的咱们应该怎么打?——按加血量从大到小排序

假如,存在一只怪兽不加血,那么咱们留到最后再打,例以下

①初始血量:13 第一个怪兽血量:12 掉落血瓶:8;第二个怪兽血量:1 掉落血瓶:0

对于这样的状况,咱们是先打第一只再打第二只。

①初始血量:15 第一个怪兽血量:13 掉落血瓶:0;第二个怪兽血量:2 掉落血瓶:1

对于这样的状况,咱们是先打第二只再打第一只。

关于“打会掉血的怪兽,咱们按照加血量从小到大排序”这种打法的证实:

每一个数字都减去最小值和原问题等价,咱们能肯定最后打哪只怪兽。

对于剩下的n-1只怪兽,再都减去最小值,咱们能肯定倒数第二次打哪只怪兽。

一样对于剩下的n-2只怪兽……

以下面这种状况:

初始血量:15 第一个怪兽血量:14 掉落血瓶:10;第二个怪兽血量:3 掉落血瓶:2

确定是先打第一只怪兽,而后打第二只怪兽。

15-14+10-3+2=10

15          =10-2+3-10+14(移项)

从上式咱们就能够看出:对于会扣你血的怪兽,换个角度思考,从最后一只怪兽开始,回的血至关于扣的血,扣的血至关于回的血,此时和第一种状况是同样的。
所以按照
加血量从小到大排序
就能够了(理解:把等号左边的移动右边)

因此,在打会掉血的怪兽时,按照加血量从小到大排,这样打必定是最优的。

若是按照这种策略打不过了,实际上按照任何一种策略都打不过。

经典例题:销售比赛(野题)

有n天(n是偶数),第i天的物品价格为ai,每一天你能够选择买一件或者卖一件物品,而且必须选择。

初始时有花不完的钱,问n天后最多盈利多少钱。

解题思路

首先须要明确的一点是,第一天确定得买,最后一天确定得卖,且最终全部物品都刚好卖完。

那么如今咱们来看次日和第三天的物品,确定拿价格大的卖,价格小的买(不考虑同时买)。

接下来的每两天中,若价格小的那个价格都比以前卖出的物品价格高,则将以前的那个物品买进,这个物品卖出。

这个贪心显然是正确的(验证一下真的很“显然”)。

经典例题:残缺方块问题(野题)

有一个2k×2k的棋盘,其中刚好有一个方块残缺。如下是k=1时的4种状况。

咱们须要经过若干三格板(可旋转)来覆盖这个棋盘,要求任意两块三格板不重叠,且三格板不覆盖残缺的方块。输出一种方案便可。

解题思路

考虑分治算法,将一个k=i的问题转化为4个k=i-1的子问题,以下图:

      

其中每一部分均变成了一个子问题,而咱们知道k=1时能够直接构造,如此分治便可。

4、ZHW最后的吹水:

1.NOIP洛谷上刷满500题,提升组一等奖没问题。。

2.对数字要有敏感,例如:

128M=134217728Byte

    =(134217728/8)个long long类型数据

    =(134217728/4)个int类型数据

    =(134217728/2)个short类型数据

    =(134217728/1)个bool、char类型数据

3.平时写题多写写对拍,试一下大数据和比较刁难的数据。

4.刷题,历年NOIP均可以拿到400分,就很稳;保证一等奖的话,至少350分。

5.信息学奥赛和咱们的数学英语不同,你随便乱选几个,均可以拿到十几二十分这样,但在OI中,呵呵,爆内存的话100分直接变成0分!

6道题,7个小时,难度较高,要作到,在平时的比赛,碾压别人

6.辣鸡的作题顺序:看到一道题→想了一下子不会作→网上找题解会作了懒得作了copy一下交上去炉石!

7.对题目中数据的范围要有敏感,例如:

N=100w,O(n)                N=10w,O(nlgn)或O(nlg2n)

N=30w,O(nlgn)              N=5w,O(n×sqrt(n))

N=5000,O(n2)               N=300,O(n3)

N=1000,O(n2×lgn)          N=100,O(n4)

N=40,O(2(n/2)×n)           N=35,O(2(n/2)×n×lg)

N=20,O(2n×n)              N=10,O(n!)

最近发现一些网站盗用个人blog,这实在不能忍(™把关于个人名字什么的所有删去只保留文本啥意思。。)!!但愿各位转载引用时请注明出处,谢谢配合噢~

原博客惟一地址:http://www.cnblogs.com/geek-007/p/7296457.html

相关文章
相关标签/搜索