2018.6.29 省選模擬賽
*註意:這套題目應版權方要求,不得公示題面。
從這裏開始
- Problem A 小D與電梯
- Problem B 小D與田野
- Problem C 小D與函數
Problem A 小D與電梯
題目大意
假設電梯在0層,容量無限大。給定$n$個人的到達時間和要去的樓層。電梯上下一層樓花費的時間為1,電梯開關門、乘客上下的耗時不計,電梯可以停留在某一層樓。問將所有人送達到目的地,並讓電梯回到0層的最少耗時。
先按到達時間將所有人排序。顯然,每次電梯運輸的人是一段連續的區間中的人。
用$f[i]$表示將前$i$個人送到目的地,並且電梯回到0層的最少耗時。
轉移方程顯然:$f[i] = \min\left \{ \max(f[j], arrive[i]) + 2 \times max_{j < k \leqslant i}floor[k] \right \}$。
性質1 $f[i]\leqslant f[i + 1]$。
將階段$i + 1$的策略去掉第$i + 1$個人,作用於階段$i$。
因此,可以二分出一個分界點,使得前一部分取$arrive[i]$,另一部分取$f[j]$。
對於前一部分,根據後面半個式子隨$j$減小而不下降可以知道,取分界點的時候最優。
對於後一部分,考慮使用一個單調棧維護走的樓層,用線段樹維護動態規劃的轉移。必要時進行修改。
總時間復雜度$O(n\log n)$。
考場上很好玩的是,前一部分寫錯了二分判斷條件(多打了個‘=‘),然後mcfx坑爹的subtask評測,小數據的subtask沒有過,大數據直接skip掉。喜聞樂見A題爆零。測完後,$n$小於等於5000的數據用$O(n^2)$的動態規劃,剩下的部分線段樹,然後就AC。內心有句mmp。
Code
1 #include <algorithm> 2 #include <iostream> 3 #include <cassert> 4 #include <cstdlib> 5Problem A#include <cstdio> 6 #ifndef WIN32 7 #define Auto "%lld" 8 #else 9 #define Auto "%I64d" 10 #endif 11 using namespace std; 12 typedef bool boolean; 13 #define pii pair<int, int> 14 #define fi first 15 #define sc second 16 #define ll long long 17 18 const signed ll llf = (signed ll) (~0ull >> 1); 19 const int N = 1e6 + 5; 20 21 typedef class SegTreeNode { 22 public: 23 ll val, lazy; 24 SegTreeNode *l, *r; 25 26 SegTreeNode():val(0), lazy(0), l(NULL), r(NULL) { } 27 28 void pushUp() { 29 val = min(l->val, r->val); 30 } 31 32 void pushDown() { 33 l->val += lazy, r->val += lazy; 34 l->lazy += lazy, r->lazy += lazy; 35 lazy= 0; 36 } 37 }SegTreeNode; 38 39 SegTreeNode pool[4 * N]; 40 SegTreeNode *top = pool; 41 42 SegTreeNode* newnode() { 43 return top++; 44 } 45 46 typedef class SegTree { 47 public: 48 int n; 49 SegTreeNode* rt; 50 51 SegTree() { } 52 SegTree(int n):n(n) { 53 build(rt, 1, n); 54 } 55 56 void build(SegTreeNode* &p, int l, int r) { 57 p = newnode(); 58 if (l == r) return ; 59 int mid = (l + r) >> 1; 60 build(p->l, l, mid); 61 build(p->r, mid + 1, r); 62 } 63 64 void update(SegTreeNode *p, int l, int r, int ql, int qr, ll val) { 65 if (l == ql && r == qr) { 66 p->val += val, p->lazy += val; 67 return ; 68 } 69 if (p->lazy) p->pushDown(); 70 int mid = (l + r) >> 1; 71 if (qr <= mid) 72 update(p->l, l, mid, ql, qr, val); 73 else if (ql > mid) 74 update(p->r, mid + 1, r, ql, qr, val); 75 else { 76 update(p->l, l, mid, ql, mid, val); 77 update(p->r, mid + 1, r, mid + 1, qr, val); 78 } 79 p->pushUp(); 80 } 81 82 ll query(SegTreeNode *p , int l, int r, int ql, int qr) { 83 if (l == ql && r == qr) 84 return p->val; 85 if (p->lazy) p->pushDown(); 86 int mid = (l + r) >> 1; 87 if (qr <= mid) 88 return query(p->l, l, mid, ql, qr); 89 if (ql > mid) 90 return query(p->r, mid + 1, r, ql, qr); 91 ll a, b; 92 a = query(p->l, l, mid, ql, mid); 93 b = query(p->r, mid + 1, r, mid + 1, qr); 94 return (a < b) ? (a) : (b); 95 } 96 97 void update(int l, int r, ll val) { 98 // cerr << "[" << l << ", " << r << "] added " << val << endl; 99 update(rt, 1, n, l, r, val); 100 } 101 102 ll query(int l, int r) { 103 return query(rt, 1, n, l, r); 104 } 105 }SegTree; 106 107 int n, tp = -1; 108 ll f[N]; 109 pii ps[N]; 110 SegTree st; 111 pii pst[N]; 112 113 inline void init() { 114 scanf("%d", &n); 115 for (int i = 1; i <= n; i++) 116 scanf("%d%d", &ps[i].fi, &ps[i].sc); 117 } 118 119 inline void solve() { 120 ps[0].fi = ps[0].sc = 0; 121 sort(ps + 1, ps + n + 1); 122 f[0] = 0, st = SegTree(n + 1); 123 for (int i = 1, l, r, di, mid; i <= n; i++) { 124 l = 0, r = i - 1; 125 while (l <= r) { 126 mid = (l + r) >> 1; 127 if (f[mid] <= ps[i].fi) 128 l = mid + 1; 129 else 130 r = mid - 1; 131 } 132 di = l - 1; 133 while (~tp && pst[tp].fi < ps[i].sc) { 134 st.update((tp) ? (pst[tp - 1].sc + 1) : (1), pst[tp].sc, (ps[i].sc - pst[tp].fi) * 2ll); 135 tp--; 136 } 137 pst[++tp] = pii(ps[i].sc, i); 138 st.update(i, i, ps[i].sc * 2ll); 139 l = 0, r = tp; 140 while (l <= r) { 141 mid = (l + r) >> 1; 142 if (pst[mid].sc > di) 143 r = mid - 1; 144 else 145 l = mid + 1; 146 } 147 f[i] = ps[i].fi + pst[r + 1].fi * 2ll; 148 // cerr << "A " << f[i] << " " << pst[r + 1].fi << " " << pst[r + 1].sc << endl; 149 // cerr <<"mid: " << di << " f: " << f[i] << " " << pst[r + 1].fi << endl; 150 if (di < i - 1) { 151 ll cmp = st.query(di + 2, i); 152 f[i] = min(f[i], cmp); 153 } 154 st.update(i + 1, i + 1, f[i]); 155 // cerr << f[i] << endl; 156 } 157 printf(Auto, f[n]); 158 } 159 160 int main() { 161 init(); 162 solve(); 163 return 0; 164 }
Problem B 小D與田野
題目大意
給定一個有向圖的鄰接矩陣$G$,其中$G_{i, j}$表示從$i$到$j$的有向邊的數量。多次詢問從$u$到$v$所有長度不超過$k - 1$的路徑的代價和除以998244353的余數。定義一條長度為$L$的路徑的代價為$L^{T}$。一條路的路徑長度定義為它經過有向邊的數量。
題目等價於求出$G + 2^{T}G^{2} + 3^{T}G^{3} + \cdots + (k - 1)^{T}G^{k - 1}$。
我們知道當$T = 0$的時候,可以用簡單的分治水掉。
然後開始思考$T > 1$的時候。
引理1 若$n, k$均為正整數,則$n^{k}=\sum_{i = 1}^{n}S_{k}^{i}\times i!\times C_{n}^{i}$。其中$S_{k}^{i}表示第二類Stirling數$。
證明 考慮$n^{k}$的組合意義:把$k$個不同的球放入$n$個不同的盒子中的方案數。
$S_{k}^{i}$的組合意義是將$k$個不同的球放入$i$個相同的盒子中,並且每個盒子非空的方案數。那麽等號右邊等價於枚舉恰好有多少個不同盒子是非空的,然後將$k$個球放入其中。顯然它們是等價的。
又因為,當$n < k$的時候$S_{n}^{k} = 0$,因此,需要求出的等價於$\sum_{i=1}^{n}\sum_{j = 0}^{T}S_{T}^{j}j! C_{i}^{j}G^{i}=\sum_{j = 0}^{T}S_{T}^{j}j!\sum_{i = 1}^{n} C_{i}^{j}G^{i}$。然後求出當$j = 0, 1, 2, \cdots, T$時,後面的和。繼續考慮分治,假設我已經求出了當$1\leqslant i\leqslant mid$的時候的$T + 1$個和。為了求出後面部分的和,考慮使用下面這個結論:
引理2(範德蒙卷積) $C_{n + m} ^ {k} = \sum_{i = 0}^{k}C_{n}^{i}C_{m}^{k - i}$
證明 這裏只介紹組合意義的證明。
考慮$n + m$個物品中選出$k$個物品,一定從其中$n$個物品中選出了$i$個,然後從另外$m$個物品中選出了$k - i$個物品。式子中枚舉了所有可能的$i$,因此等式成立。
現在假設要求計算的前$r$個的和,中的$r$是偶數。因為如果它是奇數,可以手動補上最後沒有被計算的一項。於是有:
$\sum_{i = mid + 1}^{r}C_{i}^{j}G^{i}\\=G^{mid}\sum_{i=1}^{mid}C_{i + mid}^{j}G^{i}\\=G^{mid}\sum_{i=1}^{mid}\sum_{k = 0}^{j}C_{i}^{k}C_{mid}^{j - k}G^{i}\\=G^{mid}\sum_{k = 0}^{j}C_{mid}^{j - k}\left(\sum_{i = 1}^{mid}C_{i}^{k}G^{i} \right )$
最終式子括號內的是已經計算出來的內容。對於$G^{mid}$可以隨著分治一起計算。
因此總時間復雜度為$O(n^{3}\log K + T^{2}n^{2}\log K)$。感謝Joker提供此做法,完爆標程$O(n^{3}T\log K)$。
Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 using namespace std; 5 typedef bool boolean; 6 7 const int N = 73, M = 998244353; 8 int n, k, q, T; 9 10 void exgcd(int a, int b, int& x, int& y) { 11 if (!b) 12 x = 1, y = 0; 13 else { 14 exgcd(b, a % b, y, x); 15 y -= (a / b) * x; 16 } 17 } 18 19 int inv(int a, int n) { 20 int x, y; 21 exgcd(a, n, x, y); 22 return (x < 0) ? (x + n) : (x); 23 } 24 25 typedef class Matrix { 26 public: 27 int ar[N][N]; 28 29 Matrix operator * (Matrix b) { 30 Matrix rt; 31 for (int i = 1; i <= n; i++) 32 for (int j = 1; j <= n; j++) { 33 rt[i][j] = 0; 34 for (int k = 1; k <= n; k++) 35 rt[i][j] = (rt[i][j] + ar[i][k] * 1ll * b[k][j]) % M; 36 } 37 return rt; 38 } 39 40 Matrix operator + (Matrix b) { 41 Matrix rt; 42 for (int i = 1; i <= n; i++) 43 for (int j = 1; j <= n; j++) 44 rt[i][j] = (ar[i][j] + b[i][j]) % M; 45 return rt; 46 } 47 48 Matrix operator * (int b) { 49 Matrix rt; 50 for (int i = 1; i <= n; i++) 51 for (int j = 1; j <= n; j++) 52 rt[i][j] = (ar[i][j] * 1ll * b) % M; 53 return rt; 54 } 55 56 int* operator [] (int pos) { 57 return ar[pos]; 58 } 59 60 void debugOut() { 61 for (int i = 1; i <= n; i++, cerr << endl) 62 for (int j = 1; j <= n; j++) 63 cerr << ar[i][j] << " "; 64 } 65 }Matrix; 66 67 Matrix g; 68 69 inline void init() { 70 scanf("%d%d%d%d", &n, &k, &q, &T); 71 for (int i = 1; i <= n; i++) 72 for (int j = 1; j <= n; j++) 73 scanf("%d", g[i] + j); 74 } 75 76 Matrix pa; 77 Matrix ms[22][6]; 78 Matrix* dividing(int dep, int n) { 79 Matrix *rt = ms[dep]; 80 if (n == 1) { 81 pa = g, rt[0] = rt[1] = g; 82 for (int i = 2; i <= T; i++) 83 rt[i] = g * 0; 84 return rt; 85 } 86 int mid = n >> 1; 87 dividing(dep + 1, mid); 88 Matrix *ar = ms[dep + 1]; 89 rt[0] = ar[0] * pa + ar[0]; 90 int Cs[T + 1], C = 1; 91 Cs[0] = 1; 92 for (int i = 1; i <= T; i++) 93 Cs[i] = (Cs[i - 1] * 1ll * (mid + 1 - i) % M) * 1ll * inv(i, M) % M; 94 for (int k = 1; k <= T; k++) { 95 rt[k] = g * 0; 96 for (int j = 0; j <= k; j++) 97 rt[k] = rt[k] + ar[j] * Cs[k - j]; 98 rt[k] = rt[k] * pa + ar[k]; 99 } 100 pa = pa * pa; 101 if (n & 1) { 102 pa = pa * g; 103 rt[0] = rt[0] + pa; 104 for (int i = 1; i <= T; i++) { 105 C = (C * 1ll * (n + 1 - i) % M) * 1ll * inv(i, M) % M; 106 rt[i] = rt[i] + pa * C; 107 } 108 } 109 return rt; 110 } 111 112 const int S[6][6] = {{1}, {0, 1}, {0, 1, 2}, {0, 1, 6, 6}, {0, 1, 14, 36, 24}, {0, 1, 30, 150, 240, 120}}; 113 Matrix res; 114 115 inline void solve() { 116 Matrix* rts = dividing(0, k - 1); 117 int u, v; 118 res = g * 0; 119 for (int i = 0; i <= T; i++) 120 res = res + rts[i] * S[T][i]; 121 while (q--) { 122 scanf("%d%d", &u, &v); 123 printf("%d\n", res[u][v] + (u == v && T == 0)); 124 } 125 } 126 127 int main() { 128 init(); 129 solve(); 130 return 0; 131 }Problem B
Problem C 小D與函數
題目大意
設$F_{k}(n) = \sum_{i = 1}^{n}i^{k}, G_{k}(n) = \sum_{i = 1}^{n}F_{k}(n), H_{k}(n) = \sum_{i = 0}^{n}G(A + i\cdot d)$。給定常數$k,n,A,d,p$,分別求出$F_{k}(n), G_{k}(n), H_{k}(n)$除以$p$的余數,保證$p$是一個質數。
根據差分分別證得三個函數是關於$n$的$k + 1, k + 2, k + 3$次多項式。
然後逐差法水過。時間復雜度$O(k^{2}T)$。
Code
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 using namespace std; 5 typedef bool boolean; 6 7 #define ll long long 8 const int K = 1015; 9 10 void exgcd(int a, int b, int& d, int& x, int& y) { 11 if (!b) 12 d = a, x = 1, y = 0; 13 else { 14 exgcd(b, a % b, d, y, x); 15 y -= (a / b) * x; 16 } 17 } 18 19 int inv(int a, int n) { 20 int d, x, y; 21 exgcd(a, n, d, x, y); 22 return (x < 0) ? (x + n) : (x); 23 } 24 25 int qpow(int a, int pos, int p) { 26 int pa = a, rt = 1; 27 for ( ; pos; pos >>= 1, pa = pa * 1ll * pa % p) 28 if (pos & 1) 29 rt = rt * 1ll * pa % p; 30 return rt; 31 } 32 33 int T; 34 int k, n, A, d, p; 35 36 inline void init() { 37 scanf("%d%d%d%d%d", &k, &n, &A, &d, &p); 38 } 39 40 int ds[K][K]; 41 int cs[K], ncs[K]; 42 43 void getDelta(int *cs, int k) { 44 for (int i = 1; i <= k; i++) 45 for (int j = 0; j <= k - i; j++) 46 ds[i][j] = (ds[i - 1][j + 1] * 1ll - ds[i - 1][j] + p) % p; 47 for (int i = 0; i <= k; i++) 48 cs[i] = ds[i][0]; 49 } 50 51 int invs[K]; 52 53 void prepare() { 54 for (int i = 1; i <= k + 10; i++) 55 invs[i] = inv(i, p); 56 } 57 58 int G(ll n) { 59 int pn = n % p; 60 int C = ((pn + 2) * 1ll * (pn + 1) / 2) % p, res = C * 1ll * cs[0] % p; 61 for (int i = 1; i <= k; i++) { 62 C = (C * 1ll * ((pn + 1ll - i + p) % p) % p) * invs[i + 2] % p; 63 res = (res + C * 1ll * cs[i]) % p; 64 } 65 return res; 66 } 67 68 inline void solve() { 69 ds[0][0] = 0; 70 for (int i = 1; i <= k; i++) 71 ds[0][i] = qpow(i, k, p); 72 getDelta(cs, k); 73 int C = n + 1, res = C * 1ll * cs[0] % p, ans; 74 for (int i = 1; i <= k; i++) { 75 C = (C * 1ll * (n + 1 - i) % p) * invs[i + 1] % p; 76 res = (res + C * 1ll * cs[i]) % p; 77 } 78 for (int i = 0; i <= k + 8; i++) 79 ds[0][i] = G(A + d * 1ll * i); 80 getDelta(ncs, k + 8); 81 C = n + 1, ans = C * 1ll * ncs[0] % p; 82 for (int i = 1; i <= k + 8; i++) { 83 C = (C * 1ll * (n + 1 - i) % p) * invs[i + 1] % p; 84 ans = (ans + C * 1ll * ncs[i]) % p; 85 } 86 printf("%d %d %d\n", res, G(n), ans); 87 } 88 89 int main() { 90 scanf("%d", &T); 91 while (T--) { 92 init(); 93 prepare(); 94 solve(); 95 } 96 return 0; 97 }Problem C
2018.6.29 省選模擬賽