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



第一題可以說的內容不是很多吧。直接暴力,計算每種鉛筆需要花費的金額。
只不過計算的時候,需要注意如下問題
- 如果不是整數倍,除完后要加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小時內)
處理一艘船的時候,解決如下幾個任務
- 更新對於已經不在24小時內的船,從last開始while循環,減去它們對應的國籍的游客數,再判斷它是否為0,如果是,那么就把當前不同國籍的數量減去1
- 讀入該艘船上的游客,如果這個國籍的數量為0,則把當前不同國籍的數量數加1,接着把這個國籍的游客數加1
- 輸出這個不同國籍的數量
關於還省內存的事再說一下
- 使用vector
- 用兩個一維數組,一個記錄每艘船記錄的數據在另一個數組中結束的下標,另一個就記錄每艘船的游客的國籍
由於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 }
正解呢就是很神奇的方法來做的,首先看張圖

設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分好多了(真的沒有別的意思),所以說壓力沒那么大,而且靠差了還有提高組
