noip2016普及組題解和心得


前言

  感覺稍微有些滑稽吧,畢竟每次練的題都是提高組難度的,結果最后的主要任務是普及組抱一個一等獎回來。至於我的分數嘛。。還是在你看完題解后寫在[后記]里面。廢話不多說,開始題解。

  (其實這篇博客只有題解沒有心得。)



  第一題可以說的內容不是很多吧。直接暴力,計算每種鉛筆需要花費的金額。

  只不過計算的時候,需要注意如下問題

  • 如果不是整數倍,除完后要加1
  • 神奇的Linux系統,很多人的第三個點wa了,所以要養成良好的編寫代碼的習慣

Code

 1 #include<iostream>
 2 #include<fstream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<algorithm>
 8 #include<stack>
 9 #include<set>
10 #include<map>
11 #include<queue>
12 using namespace std;
13 typedef bool boolean;
14 #define smin(a, b) (a) = min((a), (b))
15 #define smax(a, b) (a) = max((a), (b))
16 template<typename T>
17 inline void readInteger(T& u){
18     char x;
19     int aFlag = 1;
20     while(!isdigit((x = getchar())) && x != '-');
21     if(x == '-'){
22         aFlag = -1;
23         x = getchar();
24     }
25     for(u = x - '0'; isdigit((x = getchar())); u = u * 10 + x - '0');
26     ungetc(x, stdin);
27     u *= aFlag;
28 }
29 
30 int n;
31 int cost[5], num[5];
32 int result;
33 
34 inline void init(){
35     result = 0xfffffff;
36     readInteger(n);
37     for(int i = 1, b; i <= 3; i++){
38         b = 0;
39         readInteger(num[i]);
40         readInteger(cost[i]);
41         b = n / num[i];
42         if(n % num[i] != 0)    b += 1;
43         smin(result, b * cost[i]);
44     }
45     printf("%d", result);
46 }
47 
48 int main(){
49     freopen("pencil.in", "r", stdin);
50     freopen("pencil.out", "w", stdout);
51     init();
52     return 0;
53 }

 


  第二題,暴力也不會TLE,所以就從起始日期枚舉到結束日期,中途把進位之類的事情做好,問題就不會很大。

  只不過仍然會存在一些問題

  • 進位應該從低位開始判斷
  • 注意進位的邊界

   如果不小心手抽了,有些日期跳過了,或者有些地方出了點問題,變成了死循環,也很麻煩。

Code

  1 #include<iostream>
  2 #include<fstream>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<cstdlib>
  6 #include<cmath>
  7 #include<algorithm>
  8 #include<stack>
  9 #include<set>
 10 #include<map>
 11 #include<queue>
 12 using namespace std;
 13 typedef bool boolean;
 14 #define smin(a, b) (a) = min((a), (b))
 15 #define smax(a, b) (a) = max((a), (b))
 16 template<typename T>
 17 inline void readInteger(T& u){
 18     char x;
 19     int aFlag = 1;
 20     while(!isdigit((x = getchar())) && x != '-');
 21     if(x == '-'){
 22         aFlag = -1;
 23         x = getchar();
 24     }
 25     for(u = x - '0'; isdigit((x = getchar())); u = u * 10 + x - '0');
 26     ungetc(x, stdin);
 27     u *= aFlag;
 28 }
 29 
 30 typedef class MyDate{
 31     public:
 32         int year;
 33         int month;
 34         int days;
 35         MyDate(){}
 36         MyDate(int num){
 37             days = num % 100, num /= 100;
 38             month = num % 100, num /= 100;
 39             year = num;
 40         }
 41         boolean isHuiWen(){
 42             if((year / 1000) != (days % 10))    return false;
 43             if(((year / 100) % 10) != ((days / 10) % 10))    return false;
 44             if(((year / 10) % 10) != (month % 10))    return false;
 45             if((year % 10) != ((month / 10) % 10))    return false;
 46             return true;
 47         }
 48         void next(){
 49             days++;
 50             if(month == 2 && days == 30 && (year % 100 != 0 && (year % 4 == 0 || year % 400 == 0))){
 51                 days = 1;
 52                 month++;
 53             }else if(month == 2 && days == 29 && (!(year % 100 != 0 && (year % 4 == 0 || year % 400 == 0)))){
 54                 days = 1;
 55                 month++;
 56             }else if(days == 32 && (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)){
 57                 days = 1;
 58                 month++;
 59             }else if(days == 31 && (month == 4 || month == 6 || month == 9 || month == 11)){
 60                 days = 1;
 61                 month++;
 62             }
 63             if(month == 13){
 64                 month = 1;
 65                 year++;
 66             }
 67         }
 68         boolean operator !=(MyDate another){
 69             return !(this->days == another.days && this->month == another.month && this->year == another.year);
 70         }
 71 }MyDate;
 72 
 73 MyDate start;
 74 MyDate _end;
 75 
 76 inline void init(){
 77     int a, b;
 78     readInteger(a);
 79     readInteger(b);
 80     start = MyDate(a);
 81     _end = MyDate(b);
 82 }
 83 
 84 inline void solve(){
 85     int result = 0;
 86     while(start != _end){
 87         if(start.isHuiWen())    result++;
 88         start.next();
 89     }
 90     if(_end.isHuiWen())    result++;
 91     printf("%d", result);
 92 }
 93 
 94 int main(){
 95     freopen("date.in", "r", stdin);
 96     freopen("date.out", "w", stdout);
 97     init();
 98     solve();
 99     return 0;
100 }

[小結]

  這道題的難度不是特別難,但是,有個很明顯的問題代碼不簡潔,而且好像讀入優化也沒什必要。。

明明70幾行可以打完的程序,我卻偏偏打了100行。

  如果是C語言用戶,可以手打一些函數來代替我寫的成員函數


 

 


  第三題是暴力加優化。

  首先講思路吧。從頭到尾掃描。需要一些量比如說上一艘船,對於它來說,在24小時內最早的一艘的位置(數組中)(就記為last吧)。還有當前不同種類的游客。還需要統計每個國籍的游客的數量(在這艘船的24小時內)

處理一艘船的時候,解決如下幾個任務

  1. 更新對於已經不在24小時內的船,從last開始while循環,減去它們對應的國籍的游客數,再判斷它是否為0,如果是,那么就把當前不同國籍的數量減去1
  2. 讀入該艘船上的游客,如果這個國籍的數量為0,則把當前不同國籍的數量數加1,接着把這個國籍的游客數加1
  3. 輸出這個不同國籍的數量

 關於還省內存的事再說一下

  1. 使用vector
  2. 用兩個一維數組,一個記錄每艘船記錄的數據在另一個數組中結束的下標,另一個就記錄每艘船的游客的國籍

  由於vector太慢(雖說對於這道題已經足夠了),而且經常手抖導致崩掉,果斷嫌棄,改用方法2(我也不是說vector一定不好,至少對於很多時候還是挺管用的,效率也沒有我說的那么糟糕,只不過我不喜歡而已)

  當然現在我很喜歡用vector偷懶  ——2018.2.13

Code

 1 #include<iostream>
 2 #include<fstream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<algorithm>
 8 #include<stack>
 9 #include<set>
10 #include<map>
11 #include<queue>
12 #include<vector>
13 using namespace std;
14 typedef bool boolean;
15 #define smin(a, b) (a) = min((a), (b))
16 #define smax(a, b) (a) = max((a), (b))
17 template<typename T>
18 inline void readInteger(T& u){
19     char x;
20     int aFlag = 1;
21     while(!isdigit((x = getchar())) && x != '-');
22     if(x == '-'){
23         aFlag = -1;
24         x = getchar();
25     }
26     for(u = x - '0'; isdigit((x = getchar())); u = u * 10 + x - '0');
27     ungetc(x, stdin);
28     u *= aFlag;
29 }
30 
31 #define DAYSEC    86400
32 
33 int n;
34 int tbegin;
35 int *times;
36 int listofx[300005];
37 int counter[100005];
38 int defkinds;
39 int *endsindex;
40 
41 inline void init(){
42     readInteger(n);
43     endsindex = new int[(const int)(n + 1)];
44     times = new int[(const int)(n + 1)];
45     tbegin = 1;
46     endsindex[0] = 0;
47     for(int i = 1, peo; i <= n; i++){
48         readInteger(times[i]);
49         while(times[tbegin] <= times[i] - DAYSEC){
50             for(int j = endsindex[tbegin - 1] + 1; j <= endsindex[tbegin]; j++){
51                 counter[listofx[j]]--;
52                 if(counter[listofx[j]] <= 0)    defkinds--;
53             }
54             tbegin++;
55         }
56         readInteger(peo);
57         endsindex[i] = endsindex[i - 1] + peo;
58         for(int j = endsindex[i - 1] + 1; j <= endsindex[i]; j++){
59             readInteger(listofx[j]);
60             if(counter[listofx[j]] == 0)    defkinds++;
61             counter[listofx[j]]++;
62         }
63         printf("%d\n", defkinds);
64     }
65 }
66 
67 int main(){
68     freopen("port.in", "r", stdin);
69     freopen("port.out", "w", stdout);
70     init();
71     return 0;
72 }

 

 


  首先呢,想一個正確但不高效的方法,枚舉a,b,c,d然后判斷是否存在,理論復雜度O(m4)。

  接着可以發現一個物體的魔法值為2,還有一個魔法值為2,它們的貢獻(實在找不到詞了),是一樣的。所以可以直接枚舉數值,並且統計了數量后,可以O(1)判斷d是否存在,復雜度降為O(4n3m)

  看着4m有些不爽,決定把它優化掉。既然魔法值一樣的物品貢獻一樣就沒必要按照每件來記錄作為A、B、C、D物品的次數,直接按照數值來記錄,就沒有必要去用第四重循環了。

  計算的方法是這樣的作為a物品的次數,根據乘法原理,就要加上可作為b物品、c物品和d物品的個數的乘積。沒怎么說清楚,但是舉個例子就好懂,比如說有1,5,24,26,26。當1作為a物品時,可作為b物品的是5,有一個,可作為c物品的是24,有一個,可作為d物品的是26,有兩個,所以應該增加1*1*2次。其他同理。

  為了對付極端數據(不過貌似沒有),所以我拍了序,加了個lower_bound,然后並沒有AC(滑稽)

  1 #include<iostream>
  2 #include<fstream>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<cstdlib>
  6 #include<cmath>
  7 #include<algorithm>
  8 #include<stack>
  9 #include<set>
 10 #include<map>
 11 #include<queue>
 12 #include<ctime>
 13 using namespace std;
 14 typedef bool boolean;
 15 #define smin(a, b) (a) = min((a), (b))
 16 #define smax(a, b) (a) = max((a), (b))
 17 template<typename T>
 18 inline void readInteger(T& u){
 19     char x;
 20     int aFlag = 1;
 21     while(!isdigit((x = getchar())) && x != '-');
 22     if(x == '-'){
 23         aFlag = -1;
 24         x = getchar();
 25     }
 26     for(u = x - '0'; isdigit((x = getchar())); u = u * 10 + x - '0');
 27     ungetc(x, stdin);
 28     u *= aFlag;
 29 }
 30 
 31 int n, m;
 32 int counter[15005];
 33 vector<int> poses[15005];
 34 int    *fora, *forb, *forc, *ford;
 35 int *mlist;
 36 int *blist;
 37 
 38 int mylower_bound(int* a, int from, int end, int val){
 39     int l = from, r = end - 1;
 40     while(l <= r){
 41         int mid = (l + r) >> 1;
 42         if(val <= a[mid])    r = mid - 1;
 43         else l = mid + 1;
 44     }
 45     return r + 1;
 46 }
 47 
 48 inline void init(){
 49     readInteger(n);
 50     readInteger(m);
 51     fora = new int[(const int)(n + 1)];
 52     forb = new int[(const int)(n + 1)];
 53     forc = new int[(const int)(n + 1)];
 54     ford = new int[(const int)(n + 1)];
 55     mlist = new int[(const int)(m + 1)];
 56     blist = new int[(const int)(m + 1)];
 57     memset(fora, 0, sizeof(int) * (n + 1));
 58     memset(forb, 0, sizeof(int) * (n + 1));
 59     memset(forc, 0, sizeof(int) * (n + 1));
 60     memset(ford, 0, sizeof(int) * (n + 1));
 61     for(int i = 1, a; i <= m; i++){
 62         readInteger(a);
 63         counter[a]++;
 64         poses[a].push_back(i);
 65         mlist[i] = blist[i] = a;
 66     }
 67 }
 68 
 69 inline void solve(){
 70     sort(mlist + 1, mlist + m + 1);
 71     for(int i = 1; i < n; i++){
 72         if(!counter[i])    continue;
 73         for(int j = i + 2; j <= n; j += 2){
 74             if(!counter[j])    continue;
 75             int k_start = mylower_bound(mlist, 1, m + 1, j + (j - i) * 3 + 1);
 76             if(k_start == m + 1)    continue;
 77             for(int k = mlist[k_start]; k <= n; k++){
 78                 if(!counter[k])    continue;
 79                 int end = k + (j - i) / 2;
 80                 if(end > n)    break;
 81                 if(counter[end]){
 82                     fora[i] += counter[j] * counter[k] * counter[end];
 83                     forb[j] += counter[i] * counter[k] * counter[end];
 84                     forc[k] += counter[i] * counter[j] * counter[end];
 85                     ford[end] += counter[i] * counter[j] * counter[k];
 86                 }
 87             }
 88         }
 89     }
 90     for(int i = 1; i <= m; i++){
 91         printf("%d %d %d %d\n", fora[blist[i]], forb[blist[i]], forc[blist[i]], ford[blist[i]]);
 92     }
 93 }
 94 
 95 int main(){
 96     freopen("magic.in", "r", stdin);
 97     freopen("magic.out", "w", stdout);
 98     init();
 99     solve();
100     return 0;
101 }
85分比賽時寫的程序

  正解呢就是很神奇的方法來做的,首先看張圖

  設c,d之間的差為i,則可以表示為上圖。總長度大於9i。如果我們設這個長度是9i + 1,那么看,下圖

 

  a2 - a1 = 1。那么c1,d1可以和a1,b1組合,說明c2,d2也可以和a1,b1組合,也就是說,在計算c,d的時候,只需要累加a,b搭配的方案數,再按照乘法原理就可以求出來了。枚舉的量也大大減少了,只需要枚舉i和d,a。時間復雜度成功地降為可以接受的范圍。

Code(看了正解后寫出來的程序)

 1 #include<iostream>
 2 #include<fstream>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cstdlib>
 6 #include<cmath>
 7 #include<algorithm>
 8 #include<stack>
 9 #include<set>
10 #include<map>
11 #include<queue>
12 #include<ctime>
13 using namespace std;
14 typedef bool boolean;
15 #define smin(a, b) (a) = min((a), (b))
16 #define smax(a, b) (a) = max((a), (b))
17 template<typename T>
18 inline void readInteger(T& u){
19     char x;
20     int aFlag = 1;
21     while(!isdigit((x = getchar())) && x != '-');
22     if(x == '-'){
23         aFlag = -1;
24         x = getchar();
25     }
26     for(u = x - '0'; isdigit((x = getchar())); u = u * 10 + x - '0');
27     ungetc(x, stdin);
28     u *= aFlag;
29 }
30 
31 int n, m;
32 int counter[15005];
33 int    *fora, *forb, *forc, *ford;
34 int *mlist;
35 int *blist;
36 
37 int mylower_bound(int* a, int from, int end, int val){
38     int l = from, r = end - 1;
39     while(l <= r){
40         int mid = (l + r) >> 1;
41         if(val <= a[mid])    r = mid - 1;
42         else l = mid + 1;
43     }
44     return r + 1;
45 }
46 
47 inline void init(){
48     readInteger(n);
49     readInteger(m);
50     fora = new int[(const int)(n + 1)];
51     forb = new int[(const int)(n + 1)];
52     forc = new int[(const int)(n + 1)];
53     ford = new int[(const int)(n + 1)];
54     mlist = new int[(const int)(m + 1)];
55     blist = new int[(const int)(m + 1)];
56     memset(fora, 0, sizeof(int) * (n + 1));
57     memset(forb, 0, sizeof(int) * (n + 1));
58     memset(forc, 0, sizeof(int) * (n + 1));
59     memset(ford, 0, sizeof(int) * (n + 1));
60     for(int i = 1, a; i <= m; i++){
61         readInteger(a);
62         counter[a]++;
63         mlist[i] = blist[i] = a;
64     }
65 }
66 
67 inline void solve(){
68 //    sort(mlist + 1, mlist + m + 1);
69     for(int i = 1; i * 9 < n; i++){
70         int len = 9 * i + 1;
71         int s = 0;
72         for(int d = len + 1; d <= n; d++){
73             s += counter[d - len] * counter[d - len + i * 2];
74             ford[d] += s * counter[d - i];
75             forc[d - i] += s * counter[d];
76         }
77         s = 0;
78         for(int a = n - len; a >= 1; a--){
79             s += counter[a + len] * counter[a + len - i];
80             fora[a] += s * counter[a + i * 2];
81             forb[a + i * 2] += s * counter[a];
82         }
83     }
84     for(int i = 1; i <= m; i++){
85         printf("%d %d %d %d\n", fora[blist[i]], forb[blist[i]], forc[blist[i]], ford[blist[i]]);
86     }
87 }
88 
89 int main(){
90     freopen("magic.in", "r", stdin);
91     freopen("magic.out", "w", stdout);
92     init();
93     solve();
94     return 0;
95 }

PS


后話

  從上面的描述中應該可以猜到我noip普及組的分數了吧。我也就不說了。

  怎么說呢。。這也算是一個里程碑吧!雖然oi的路還長。。但是小小地慶祝一下也是可以的。

        這次考得比較好的原因主要是心態比較好吧。也感謝四川吧,分數線比起什么浙江300分好多了(真的沒有別的意思),所以說壓力沒那么大,而且靠差了還有提高組

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM