1. 程式人生 > >【九校聯考-廈門一中NOIP模擬賽】Day1總結

【九校聯考-廈門一中NOIP模擬賽】Day1總結

  離NOIP2018越來越近了,然而我仍有一些沒有徹底弄懂的地方,有點慌,所以準備從這周開搞搞基礎。

  不說別的了,先把這篇總結寫完。

 

  Day1的題感覺不是很難,每一道題的暴力也都可寫,總的來說難度跟noip差不多吧。

  有一個想吐槽的地方,就是為啥題目把演算法都告訴了……

T1 backpack

  期望得分:100

  實際得分:100

  正像題解所說,Day1T1是送分的。不過辛虧我以前做過一道類似的題,要不然只能得60。

  首先40分是送的,完全揹包板子。然後發現儘管物品很多,但是體積和價值只有100。所以如果兩件物品體積相同,那麼就取價值更大的。所以實際上物品最多隻有100個。然後一個O(100 * n)就得了60分。

  正解其實我也不知道為啥,不過以前做過一道搜尋題,那道題資料範圍特別大,但是除了暴力我實在想不出來該咋辦,然後看題解時記住了這麼一句話:“遠距離貪心,近距離暴力”。到了這道題,就變成了“大容量貪心,小容量揹包”。於是我們用價效比最高的物品填揹包,直到剩餘容量在1e5範圍內,然後正常完全揹包了。

考場程式碼

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6
#include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll;
17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxm = 1e5 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 void MYFILE() 37 { 38 #ifndef mrclr 39 freopen("backpack.in", "r", stdin); 40 freopen("backpack.out", "w", stdout); 41 #endif 42 } 43 44 int n; 45 ll m; 46 ll dp[maxm]; 47 int t[105]; 48 49 void work0() 50 { 51 ll ans = 0; 52 int pos = 1; 53 for(int i = 1; i <= 100; ++i) if((ll)t[i] * (ll)pos > (ll)t[pos] * (ll)i) pos = i; 54 ll x = (m - maxm + (ll)pos - 1) / (ll)pos; 55 m -= x * (ll)pos; 56 ans += x * (ll)t[pos]; 57 // printf("@%lld\n", ans); 58 for(int i = 1; i <= 100; ++i) 59 for(int j = i; j <= m; ++j) 60 dp[j] = max(dp[j], dp[j - i] + t[i]); 61 write(dp[m] + ans), enter; 62 } 63 64 int main() 65 { 66 MYFILE(); 67 n = read(); m = read(); 68 for(int i = 1; i <= n; ++i) 69 { 70 int x = read(), y = read(); 71 t[x] = max(t[x], y); 72 } 73 if(m > maxm) {work0(); return 0;} 74 for(int i = 1; i <= 100; ++i) 75 for(int j = i; j <= m; ++j) 76 dp[j] = max(dp[j], dp[j - i] + t[i]); 77 // for(int j = 1; j <= m; ++j) printf("%lld ", dp[j]); enter; 78 write(dp[m]), enter; 79 return 0; 80 } 81 /* 82 2 15 83 3 2 84 5 3 85 */
View Code

 

T2 sort

  期望得分:100

  實際得分:100

  講道理T2看了半天才把題看懂,因為我一直認為si可以等於tj,然後方案數總算不對。換然後看到題面說把{ci}分離成這兩個序列,才覺得不對勁。如果對於任何 i和 j,都有si != sj ,那麼樣例就解釋明白了:最大值一定是取sum[mid +1, r] - sum[l, mid];最小值一定是si和ti隔一個取一個。然後有這麼個規律:對於當前的一個點x,si已經取到的數量一定大於等於ti取得數量。這個規律不就是括號匹配的方案數嗎!答案就是卡特蘭數呀。

  然後我就忘了卡特蘭數的遞推式了。只記得有一個C(n / 2, n),於是現場2n打表前10項,一頓神推竟然推出來了。不過忘了線性求逆元,於是O(nlogn)預處理。辛虧1e6沒卡我這個。

  剩下的就是線段樹維護區間和,奇偶和了。基本跟普通的線段樹一樣,不過雖然題目保證區間長度是偶數,但是線段樹分治下去的不一定是奇數,所以pushdown的時候還要判斷一下是奇數還是偶數累加的值比區間和的一半多1個,多了得補上。

  結果剛開始我沒注意這個,以為是建樹不對,又重寫了一遍,就過更不對了。回頭又改這個,這樣就耽誤了不少時間,導致T3沒有多少時間了。

還是考場程式碼

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a, x) memset(a, x, sizeof(a))
 15 #define rg register
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const ll mod = 1e9 + 7;
 21 const int maxn = 1e6 + 5;
 22 inline ll read()
 23 {
 24     ll ans = 0;
 25     char ch = getchar(), last = ' ';
 26     while(!isdigit(ch)) {last = ch; ch = getchar();}
 27     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 28     if(last == '-') ans = -ans;
 29     return ans;
 30 }
 31 inline void write(ll x)
 32 {
 33     if(x < 0) x = -x, putchar('-');
 34     if(x >= 10) write(x / 10);
 35     putchar(x % 10 + '0');
 36 }
 37 void MYFILE()
 38 {
 39 #ifndef mrclr
 40     freopen("sort.in", "r", stdin);
 41     freopen("sort.out", "w", stdout);
 42 #endif
 43 }
 44 
 45 int n, m;
 46 
 47 ll quickpow(ll a, ll b)
 48 {
 49     a %= mod;
 50     ll ret = 1;
 51     while(b)
 52     {
 53         if(b & 1) ret = ret * a % mod;
 54         a  = a * a % mod; b >>= 1;
 55     }
 56     return ret;
 57 }
 58 ll fac[maxn], inv[maxn], cat[maxn];
 59 inline void init(const int& N)
 60 {
 61     fac[1] = 1;
 62     for(int i = 2; i <= N; ++i) fac[i] = fac[i - 1] * i % mod;
 63     inv[N] = quickpow(fac[N], mod - 2);
 64     for(int i = N - 1; i; --i) inv[i] = inv[i + 1] * (i + 1) % mod;
 65     ll x = 1;
 66     for(int i = 2; i <= N; i += 2)
 67     {
 68         x++;
 69         cat[i >> 1] = fac[i] * inv[i >> 1] % mod * inv[i >> 1] % mod * quickpow(x, mod - 2) % mod;
 70     }
 71 }
 72 
 73 int l[maxn << 2], r[maxn << 2];
 74 ll sum[maxn << 2], sum1[maxn << 2], sum0[maxn << 2], lzy[maxn << 2];
 75 void pushup(int now)
 76 {
 77     sum[now] = sum[now << 1] + sum[now << 1 | 1]; sum[now] %= mod;
 78     sum1[now] = sum1[now << 1] + sum1[now << 1 | 1]; sum1[now] %= mod;
 79     sum0[now] = sum0[now << 1] + sum0[now << 1 | 1]; sum0[now] %= mod;
 80 }
 81 void build(int L, int R, int now)
 82 {
 83     l[now] = L; r[now] = R;
 84     if(L == R)
 85     {
 86         sum[now] = read(); sum[now] %= mod;
 87         if(L & 1) sum1[now] = sum[now];
 88         else sum0[now] = sum[now];
 89         return;
 90     }
 91     int mid = (L + R) >> 1;
 92     build(L, mid, now << 1);
 93     build(mid + 1, R, now << 1 | 1);
 94     pushup(now);
 95 }
 96 void pushdown(int now)
 97 {
 98     if(lzy[now])
 99     {
100         ll ha1 = (ll)(r[now << 1] - l[now << 1] + 1) * lzy[now];
101         sum[now << 1] += ha1; sum[now << 1] %= mod;
102         if((r[now << 1] - l[now << 1] + 1) & 1)
103         {
104             if(l[now << 1] & 1) sum1[now << 1] += lzy[now];
105             else sum0[now << 1] += lzy[now];
106         }
107 //        if(l[now << 1] != r[now << 1] || (l[now << 1] == r[now << 1] && (l[now << 1] & 1)))
108             sum1[now << 1] += (ha1 >> 1); sum1[now << 1] %= mod;
109 //        if(l[now << 1] != r[now << 1] || (l[now << 1] == r[now << 1] && !(l[now << 1] & 1)))
110             sum0[now << 1] += (ha1 >> 1); sum0[now << 1] %= mod;
111         ll ha2 = (ll)(r[now << 1 | 1] - l[now << 1 | 1] + 1) * lzy[now];
112         sum[now << 1 | 1] += ha2; sum[now << 1 | 1] %= mod;
113         if((r[now << 1 | 1] - l[now << 1 | 1] + 1) & 1)
114         {
115             if(l[now << 1 | 1] & 1) sum1[now << 1 | 1] += lzy[now];
116             else sum0[now << 1 | 1] += lzy[now];
117         }    
118 //        if(l[now << 1 | 1] != r[now << 1 | 1] || (l[now << 1 | 1] == r[now << 1 | 1] && (l[now << 1 | 1] & 1)))
119             sum1[now << 1 | 1] += (ha2 >> 1); sum1[now << 1 | 1] %= mod;
120 //        if(l[now << 1 | 1] != r[now << 1 | 1] || (l[now << 1 | 1] == r[now << 1 | 1] && !(l[now << 1 | 1] & 1)))
121             sum0[now << 1 | 1] += (ha2 >> 1); sum0[now << 1 | 1] %= mod;
122         lzy[now << 1] += lzy[now]; lzy[now << 1] %= mod;
123         lzy[now << 1 | 1] += lzy[now]; lzy[now << 1] %= mod;
124         lzy[now] = 0;
125     }
126 }
127 void update(int L, int R, int now, int d)
128 {
129     if(L == l[now] && R == r[now])
130     {
131         ll ha = (ll)(R - L + 1) * d;
132         sum[now] += ha; sum[now] %= mod;
133         if((R - L + 1) & 1)
134         {
135             if(L & 1) sum1[now] += d;
136             else sum0[now] += d;
137         }
138         sum1[now] += (ha >> 1); sum1[now] %= mod;
139         sum0[now] += (ha >> 1); sum0[now] %= mod;
140         lzy[now] += d; lzy[now] %= mod;
141         return;
142     }
143     pushdown(now);
144     int mid = (l[now] + r[now]) >> 1;
145     if(R <= mid) update(L, R, now << 1, d);
146     else if(L > mid) update(L, R, now << 1 | 1, d);
147     else update(L, mid, now << 1, d), update(mid + 1, R, now << 1 | 1, d);
148     pushup(now);
149 }
150 ll query(int L, int R, int now, int flg)
151 {
152     if(L == l[now] && R == r[now]) 
153     {
154         if(flg == 0) return sum0[now];
155         else if(flg == 1) return sum1[now];
156         else return sum[now];
157     }
158     pushdown(now);    
159     int mid = (l[now] + r[now]) >> 1;
160     if(R <= mid) return query(L, R, now << 1, flg);
161     else if(L > mid) return query(L, R, now << 1 | 1, flg);
162     else return (query(L, mid, now << 1, flg) + query(mid + 1, R, now << 1 | 1, flg)) % mod;
163 }
164 
165 int main()
166 {
167     MYFILE();
168     n = read(); m = read();
169     init(n << 1);
170     build(1, n << 1, 1);
171     for(int i = 1; i <= m; ++i)
172     {
173         int d = read(), L = read(), R = read();
174         if(d == 0)
175         {
176             int val = read();
177             update(L, R, 1, val);
178         }
179         else
180         {
181             int mid = (L + R) >> 1;
182             write((query(mid + 1, R, 1, 2) - query(L, mid, 1, 2) + mod) % mod), space;
183             ll Min = query(L, R, 1, 0), Max = query(L, R, 1, 1);
184             if(L & 1) swap(Min, Max);
185             write((Max - Min + mod) % mod), space;
186             write(cat[(R - L + 1) >> 1]), enter;
187         }
188     }
189     return 0;
190 }
View Code

 

T3 digit

  期望得分:0

  實際得分:0

  其實寫暴力的時間還是有的,剩了40多分鐘。主要原因是題沒看懂:數位和到底是什麼玩意兒!?樣例模擬了半天還是不對,剩20多分鐘的時候就放棄了,因為這點時間寫生什麼都來不及了,還不如檢查T1,T2。

  現在發現T3部分分其實特別好拿。數位和就是這個數每一位上的數的和。所以30分暴力只要列舉k進位制下L位到R位的所有數即可。

  50分是一個比較水的暴dp。題目數位dp純粹和唬你的。因為4, 5, 6的最小公倍數是60,所以任何大於60的數只要對60取模就行了。令dp[i][j]表示到第 i 位,數位和 mod 60等於 j 的數的個數。那麼就有dp[i][j] = Σdp[i - 1][j - h] (0 <= h < k)。因為j - h可能為負,所以程式碼應該寫成(j - h % 60 + 60) % 60。最後的答案用字首和的思想:ans = Σdp[R][i] - Σdp[L - 1][i],i 滿足能被4,5,6其中一個整除。

  初始化應該是for(i : 0 ~k - 1) dp[1][i % 60]++。剛開始寫成了dp[1][i] = 1。只得了20分,debug可好半天。

  滿分做法:用矩乘優化。推轉移矩陣的時候磨嘰了好半天(還是不熟練,沒理解透),最後還是看了std的程式碼。然後跑兩遍快速冪。

60分程式碼

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const ll mod = 1e9 + 7;
21 const int maxn = 1e3 + 5;
22 inline ll read()
23 {
24     ll ans = 0;
25     char ch = getchar(), last = ' ';
26     while(!isdigit(ch)) {last = ch; ch = getchar();}
27     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
28     if(last == '-') ans = -ans;
29     return ans;
30 }
31 inline void write(ll x)
32 {
33     if(x < 0) x = -x, putchar('-');
34     if(x >= 10) write(x / 10);
35     putchar(x % 10 + '0');
36 }
37 void MYFILE()
38 {
39 #ifndef mrclr
40     freopen("digit.in", "r", stdin);
41     freopen("digit.out", "w", stdout);
42 #endif
43 }
44 
45 ll l, r, k;
46 ll dp[maxn][65], ans = 0;
47 
48 int main()
49 {
50     MYFILE();
51     l = read(); r = read(); k = read();
52     if(r > 1000) return 0;
53     for(rg int i = 0; i < k; ++i) dp[1][i % 60]++;
54     for(rg int i = 2; i <= r; ++i)
55         for(rg int j = 0; j < 60; ++j)
56             for(rg int h = 0; h < k; ++h)
57                 dp[i][j] += dp[i - 1][(j - h % 60 + 60) % 60], dp[i][j] %= mod;
58     for(rg int i = 0; i < 60; ++i) 
59         if(!(i % 4) || !(i % 5) || !(i % 6)) ans += dp[r][i] - dp[l - 1][i], ans %= mod;
60     write((ans - (l == 1) + mod) % mod); enter;
61     return 0;
62 }
View Code

100分程式碼

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<cstdlib>
 7 #include<cctype>
 8 #include<vector>
 9 #include<stack>
10 #include<queue>
11 using namespace std;
12 #define enter puts("") 
13 #define space putchar(' ')
14 #define Mem(a, x) memset(a, x, sizeof(a))
15 #define rg register
16 typedef long long ll;
17 typedef double db;
18 const int INF = 0x3f3f3f3f;
19 const db eps = 1e-8;
20 const ll mod = 1e9 + 7;
21 const int N = 60;
22 inline ll read()
23 {
24     ll ans = 0;
25     char ch = getchar(), last = ' ';
26     while(!isdigit(ch)) {last = ch; ch = getchar();}
27     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
28     if(last == '-') ans = -ans;
29     return ans;
30 }
31 inline void write(ll x)
32 {
33     if(x < 0) x = -x, putchar('-');
34     if(x >= 10) write(x / 10);
35     putchar(x % 10 + '0');
36 }
37 void MYFILE()
38 {
39 #ifndef mrclr
40     freopen("digit.in", "r", stdin);
41     freopen("digit.out", "w", stdout);
42 #endif
43 }
44 
45 ll l, r, k, ans = 0;
46 
47 struct Mat
48 {
49     ll a[N + 5][N + 5];
50     Mat operator * (const Mat &oth)const
51     {
52         Mat ret; Mem(ret.a, 0);
53         for(int i = 0; i < N; ++i)
54             for(int j = 0; j < N; ++j)
55                 for(int k = 0; k < N; ++k)
56                     ret.a[i][j] = (ret.a[i][j] + a[i][k] * oth.a[k][j]) % mod;
57         return ret;
58     }
59 }f;
60 
61 Mat quickpow(Mat A, ll b)
62 {
63     Mat ret; Mem(ret.a, 0);
64     for(int i = 0; i < 60; ++i) ret.a[i][i] = 1;
65     while(b)
66     {
67         if(b & 1) ret = ret * A;
68         A = A * A; b >>= 1;
69     }
70     return ret;
71 }
72 
73 int main()
74 {
75     MYFILE();
76     ll l = read(), r = read(), k = read();
77     Mem(f.a, 0);
78     for(int i = 0; i < N; ++i)
79     {
80         for(int j = 0; j < N; ++j)
81             f.a[i][j] = k / N % mod;
82         for(int j = 0; j < k % N; ++j)
83             f.a[i][(i + j) % N]++, f.a[i][(i + j) % N] %= mod;
84     }
85     Mat A = quickpow(f, r);
86     for(int i = 0; i < N; ++i)
87         if(!(i % 4) || !(i % 5) || !(i % 6)) ans = (ans + A.a[0][i]) % mod;
88     A = quickpow(f, l - 1);
89     for(int i = 0; i < N; ++i)
90         if(!(i % 4) || !(i % 5) || !(i % 6)) ans = (ans - A.a[0][i] + mod) % mod;
91     write(ans), enter;
92     return 0;
93 }
View Code

 

  Day1美中不足的地方在於T2花費的時間有點多,而這是因為沒有思考周全就開始寫程式碼了,寫完後就沒有自信,然後有點盲目的改,結果浪費了不少時間。