# 建電下學期ㄉ模擬賽題解 ## pA 鄉下人與等差數列 `Author: Lemon` 總之這不能算簽到題, 開始測資還爛掉了, 因為有人把自己題目的solution寫爛 ㄏㄏ ### Subtask 1 (30%) :::spoiler Solution $1 \leq n, m \leq 1000$ 這個測資的解很簡單w 就是硬跑一次 因為我們可以算出來每個數字要加多少 就照著加就好ㄌ :::spoiler code ```cpp= #include <iostream> using namespace std; const int MAXN = 1005; long long int a[MAXN]; void solve() { int n, m; cin >> n >> m; for(int i = 1; i <= n; ++i) { cin >> a[i]; } for(int i = 0; i < m; ++i) { int l, r, d; cin >> l >> r >> d; for(int j = l; j <= r; ++j) { a[j] += 1ll * (j - l + 1) * d; } } for(int i = 1; i <= n; ++i) { cout << a[i] << " \n"[i == n]; } } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); solve(); } ``` ::: ### Subtask 2 (20%) :::spoiler Solution $d = 1$ ~~這個Subtask其實是我亂出ㄉ~~ 基本上想到Subtask 2不太可能想不到Subtask 3(? 假設今天題目改成我們要對$\forall i \in [l, r]$ㄉ$a_i$加上$d$ 我們可以很簡單的想到要維護差分陣列 ~~不要砸BIT、線段樹~~ 例如$a = [3, 2, 1, 5, 3]$ 要對$[2, 4]$加上$1$ 我們會先對$a$做**差分**得到: $diff = [3, -1, -1, 4, -2]$ (為了方便定義$diff[0] = a[0]$) 則我們只要讓 ```cpp= diff[2] += 1, diff[5] -= 1; ``` 最後再做一次**前綴和** 就會得到新的陣列ㄌ 看回原本的題目 我們試著觀察加了一個等差數列的途中改變了什麼? 跟上面同個例子只是帶回原題 $a = [3, 2, 1, 5, 3],\ l = 2,\ r = 4,\ d = 1$ $a' = [3, 3, 3, 8, 3]$ 如果我們對他做差分ㄋ(? $diff' = [3, 0, 0, 5, -5]$ 對比原來的$diff = [3, -1, -1, 5, 3]$ 我們發現$[2, 4]$間的差增加ㄌ$1$ OAO 而$[5, 5]$間的差減少ㄌ$3$ OAO 原來是因為$[2, 4]$間加的值和前一個加的值差了1 而$[5, 5]$的改變來自於等差數列的**最後一項**! 所以**我們就可以維護$\forall i \in [l, r]$每個$diff[i]$加上$1$** 然後**只要在$diff[r + 1]$減掉等差數列的最後一項$(r - l + 1)$** ~~然後你就輕鬆ㄘTLE~~ 對一個區間的$diff$加上1 不就是區間加值ㄇ(? 再做一次**差分** ~~一樣不要線段樹或BIT~~ 最後在計算答案時先計算$a$的差分 就做完ㄌ Code放在下一個Subtask 因為基本上一樣w ::: ### Subtask 3 (50%) :::spoiler Solution 其實只要把上面所以提到$1$的地方改成$d$ 就完全okㄌ :::spoiler code ```cpp= #include <iostream> using namespace std; const int MAXN = 1e5 + 5; long long int a[MAXN]; long long int diffdiff[MAXN]; long long int diff[MAXN]; void solve() { int n, m; cin >> n >> m; for(int i = 1; i <= n; ++i) { cin >> a[i]; } for(int i = 0; i < m; ++i) { long long int l, r, d; cin >> l >> r >> d; diffdiff[l] += d; diffdiff[r + 1] -= d; diff[r + 1] -= d * (r - l + 1); } long long int prediffdiff = 0; long long int prediff = 0; for(int i = 1; i <= n; ++i) { prediffdiff += diffdiff[i]; diff[i] += prediffdiff; prediff += diff[i]; cout << a[i] + prediff << " \n"[i == n]; } } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); solve(); } ``` ::: ## pB 窩根本不會數學 `Author: Lemon` 窩根本不會數學QAQ ### Subtask 1 (5%) :::spoiler Solution $k \leq 200$ 解決$ax + by + cz = k$ 基本上很難不過(? 就照順序直接窮舉就好ㄌ 主要要注意邊界測資(有0的情況) :::spoiler code ```cpp= #include <iostream> using namespace std; void solve() { int a, b, c, k; cin >> a >> b >> c >> k; for(int x = 1; a * x <= k; ++x) { for(int y = 1; b * y <= k; ++y) { for(int z = 1; c * z <= k; ++z) { if(1ll * a * x + b * y + c * z == k) { cout << "POSSIBLE\n"; cout << x << ' ' << y << ' ' << z << '\n'; return; } if(!c) break; } if(!b) break; } if(!a) break; } cout << "IMPOSSIBLE\n"; return; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); solve(); } ``` ::: ### Subtask 2 (15%) :::spoiler Solution 當我們已經窮舉了$x, y$ 而要解決$ax + by + cz = k$ 我們可以嘗試使$cz = k - ax - by = u$ 如果 $u > 0$ 且 $c|u$ 的時候便有唯一的正整數解 $\frac{u}{c}$ 我們只需要處理前面的窮舉順序便能令字典序最小ㄌouo ~~話說我測資好像有點太水~~ :::spoiler code ```cpp= #include <iostream> using namespace std; void solve() { int a, b, c, k; cin >> a >> b >> c >> k; for(int x = 1; a * x <= k; ++x) { for(int y = 1; a * x + b * y <= k; ++y) { int val = k - (a * x + b * y); if(c) { // c != 0 if(!(val % c)) { //整除 cout << "POSSIBLE\n" << x << ' ' << y << ' ' << val / c << '\n'; return; } } else if(!val){ // c == 0 && val == 0 cout << "POSSIBLE\n" << x << ' ' << y << ' ' << 1 << '\n'; return; } if(!b) break; } if(!a) break; } cout << "IMPOSSIBLE\n"; return; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); solve(); } ``` ::: ### Subtask 3 (10%) :::spoiler Solution 既然已經知道$a = b$ 我們可以化簡式子得到$a(x + y) + cz = k$ 又因為要字典序最小$x = 1$(不然顯然可以用$y$取代) 所以使$cz = k - a(x + y) = u$ 就變成和上一題一樣的情況了 :::spoiler code ```cpp= #include <iostream> using namespace std; void solve() { int a, b, c, k; cin >> a >> b >> c >> k; // x = 1 for(int y = 1; a * (y + 1) <= k; ++y) { int val = k - a * (y + 1); if(c) { if(!(val % c)) { cout << "POSSIBLE\n1" << ' ' << y << ' ' << val / c << '\n'; return; } } else if(!val) { cout << "POSSIBLE\n" << 1 << ' ' << y << ' ' << 1 << '\n'; return; } if(!a) break; } cout << "IMPOSSIBLE\n"; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); solve(); } ``` ::: ### Subtask 4 (70%) :::spoiler Solution 這題的難度頗高 我們一樣考慮窮舉$x$ 可以得到並使$by + cz = k - ax = u$ $by + cz = u$ hmmm 似曾相似 沒錯就是貝祖定理 **一定存在$y, z$ 使 $by + cz = kgcd(b, c)$其中都是整數** 透過擴展歐幾里得算法可以解出其中一組解$(y_0, z_0)$ 我們可以嘗試透過這組解$(y_0, z_0)$找到的一種字典序最小的正整數解$(x, y)$便是答案ㄌ 啊我就不解釋ㄌ 因為窩根本不會數學。 ~~可以參考[危機百科](https://zh.wikipedia.org/zh-tw/%E8%B2%9D%E7%A5%96%E7%AD%89%E5%BC%8F)~~ ~~[這個才是Rickroll](https://www.youtube.com/watch?v=dQw4w9WgXcQ)~~ 如果窮舉完$x$仍不存在正整數解 那麼就輸出"IMPOSSIBLE"ㄅ 一樣要處理邊際測資ㄛㄛㄛ :::spoiler code ```cpp= #include <iostream> #include <algorithm> using namespace std; typedef long long int ll; ll exgcd(ll a, ll b, ll& x, ll& y) { if(a < b) return exgcd(b, a, y, x); if(!b) { x = 1; y = 0; return a; } ll ret = exgcd(b, a%b, x, y); ll temp = x; x = y; y = temp - a / b * y; return ret; } void solve() { const int INF = 1e9 + 7; int a, b, c; long long int k; cin >> a >> b >> c >> k; if(!a) a = INF; ll y0, z0; ll unit = exgcd(b, c, y0, z0); if(!unit) { if(k % a) cout << "IMPOSSIBLE\n"; else cout << "POSSIBLE\n" << k / a << ' ' << 1 << ' ' << 1 << '\n'; return; } b /= unit; c /= unit; ll mn = INF; for(int x = 1; a * x <= k; ++x) { int val = k - a * x; if(val % unit) continue; val /= unit; ll dy = val * y0, dz = val * z0; if(dz > 0) { ll d = (dz - 1) / b; dy += c * d; dz -= b * d; } if(dy > 0) { ll d = (dy - 1) / c; dy -= c * d; dz += b * d; } if(dy > 0 && dz > 0) { cout << "POSSIBLE\n"; cout << x << ' ' << dy << ' ' << dz << '\n'; return; } } cout << "IMPOSSIBLE\n"; return; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); solve(); } ``` ::: ### 酷酷DP解 :::spoiler Solution 這是吳亞倫ㄉ扣哈哈 所以可以直接去問他(? :::spoiler code ```cpp= #pragma GCC optimize("Ofast") #include <bits/stdc++.h> using namespace std; #define AI(x) begin(x),end(x) #define ll long long int #define endl '\n' #ifdef DEBUG #define debug(args...) LKJ("[ "+string(#args)+" ]", args) template<class I> void LKJ(I&&x){ cerr << x << '\n'; } template<class I, class...T> void LKJ(I&&x, T&&...t){ cerr << x << ", ", LKJ(t...); } template<class I> void OI(I a, I b){ while(a < b) cerr << *a << " \n"[next(a) == b], ++a; } #else #define debug(...) 0 #define OI(...) 0 #endif #define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); signed main() {_ int val[5]; int k; for (int i = 1; i <=3 ; i++) cin >> val[i]; cin >> k; k-=val[1]; k-=val[2]; k-=val[3]; if(k<0) { cout << "IMPOSSIBLE\n"; return 0; } // debug(k); bool dp[5][(int)1e6+5] = {0}; vector<vector<vector<int>>> source(5,vector<vector<int>>((int)1e6+5,vector<int>(3))); dp[0][0] = 1; source[0][0] = {1,1,1}; for (int i = 1; i <= 3; ++i) { for (int j = 0; j <= k; ++j) { if (j>=val[i] && dp[i][j-val[i]] && !dp[i-1][j]) { dp[i][j] = 1; source[i][j] = source[i][j-val[i]]; source[i][j][i-1]++; // debug(i,j); } else if (j>=val[i] && dp[i][j-val[i]] && dp[i-1][j]){ dp[i][j] = 1; auto tem = source[i][j-val[i]]; tem[i-1]++; source[i][j] = min(tem, source[i-1][j]); } else if (dp[i-1][j]) { dp[i][j] = 1; source[i][j] = source[i-1][j]; } } } if (!dp[3][k]){ cout << "IMPOSSIBLE\n"; return 0; } else { cout << "POSSIBLE\n"; for (int i = 0; i<3; ++i) { cout << source[3][k][i] << " "; } cout << endl; } return 0; } ``` ::: ###### tags: `CompetitiveProgramming`