1. 程式人生 > >2018.6.29 省選模擬賽

2018.6.29 省選模擬賽

operator time names sin 排序 auto lock cassert isp

*註意:這套題目應版權方要求,不得公示題面。

從這裏開始

  • 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>
  5
#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 A

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 省選模擬賽