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

相關文章
相關標籤/搜索