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>
  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

原文地址:https://www.cnblogs.com/yyf0309/p/9248156.html

时间: 2024-08-25 09:43:29

2018.6.29 省选模拟赛的相关文章

2018/3/29 省选模拟赛 80

我真是太菜了... T1 10分纯暴力没写,20分容斥也没写(人就是懒死的).还有30分矩乘不会 正解 <IOI2018 中国国家集训队第一阶段作业题解部分 - 杭州第二中学 吴瑾昭.pdf>最后一题 T2  以为自己能拿到50分,但是其实那个暴力算法只能过10分的点,n=2000的20分数据用n^3带剪枝过不了,所以拿了30分. 正解 <计数与期望问题选讲 CLJ.pdf>最后一题 我记得我以前好像看到过这个文档但是没读过..今天读一下 T3 依然是分情况50分,30分树归20分

2018.3.10 省选模拟赛

从这里开始 概况 Problem A 三元组 Problem B 攻略 Problem C 迂回 概况 这是省选T1合集?还是欢乐AK赛? 全班一半以上的人三道题都会做qwq. Doggu还剩一小时时以为自己AK了,然后玩了一小时.虽然最终被卡了20分的常数. ZJC 1个半小时AK?Excuse me? 我这条大咸鱼到最后10分钟才敲完了T1,然后发现线段树要T掉. 发自内心鄙视垃圾出题人卡常数,本来的欢乐AK变成280. 教练给我们考4个小时的试,题面上也这么写的,看题解,woc,考试时间3

2018.2.12 省选模拟赛

题目大意 (题目很简洁了,不需要大意) 其实显而易见地可以发现,当被卡一次后后面的路程都是固定了的. 可以用类似动态规划的思想来进行预处理.现在的问题就是怎么知道在某个位置刚等完红灯然后出发会在哪个路口再次被卡. 尝试画一画图: 其中横轴表示位置,纵轴表示时间,长方体表示红灯时段.有用的部分长度只有$r + g$,所以在模意义下弄一下就可以减少很多重复和无用状态: 但是这样仍然不好处理上面提到的问题,考虑让线段横着走,第一个撞着的长方形就是答案.为了实现这个目标,就每个长方形向下移动一段(移动的

2018/3/9 省选模拟赛 0分

第二题模拟扫一遍就可以过,不能更划算了.q=1的30分写的比100分还麻烦,有趣哦. 破暴力40分也没什么可写了,日常辣鸡吃枣药丸. 原文地址:https://www.cnblogs.com/137shoebills/p/8533870.html

2018.2.23 省选模拟赛

从这里开始 Problem A cycle Problem B meal Problem C naive Problem A cycle 判断是否有经过一个点的负环,我们可以跑最短路.判断这个点到自己的最短路是否是负数. 于是可以二分环大小跑dfs版spfa. 于是可以分层图做时间复杂度四方的dp. (YYR给的数据水得吓人,这两个做法居然都跑过了.然后都被我和jmr卡掉了) 注意到如果一个点走不超过$k$条边回到自己的最短路的长度是负数,那么走不超过$k + 1$条边也是. 因此我们可以二分答

2018/3/27 省选模拟赛 140分

T1 树归 100 T2 写的快速幂卷积 40,超时了,正解是矩阵乘法之类的. 正解 1 暴力(m<=5):将x的所有约数提出来矩阵乘法 2 3 定义乘法同构: 4 A=p[1]^a[1] * p[2]^a[2] * ... * p[n]^a[n] 5 B=q[1]^b[1] * q[2]^b[2] * ... * q[n]^b[n] 6 其中p[i]与q[i]皆为质数 7 将数组a与b降序排序后如果是完全相同的,那么称A与B是乘法同构的 8 如 2*2*2*2*3*3*5 与 7*11*11*

3.29省选模拟赛 矩形

LINK:矩形 一个大小为n的01方阵 m次询问 每次询问求出大小为a行b列的合法矩形的个数. 一个矩阵合法当且仅当其边缘都是为1. \(n,m\leq 1500\) 2s,256mb 考虑暴力 预处理出 r[i][j],d[i][j] 分别表示向右向下延伸的最长长度. 求答案的时候 枚举每个点 判断一下即可. 考试的时候写了一个一维的st表 和 单调队列做了这个判断. 两种方法时间复杂度一样 n^2m. 考虑优化 从第一种方法中我们考虑采用bitset来优化. b[i][j][k]表示长度>=

3.29省选模拟赛 除法与取模 dp+组合计数

LINK:除法与取模 鬼题.不过50分很好写.考虑不带除法的时候 其实是一个dp的组合计数. 考虑带除法的时候需要状压一下除法操作. 因为除法操作是不受x的大小影响的 所以要状压这个除法操作. 直接采用二进制状压是不明智的 2的个数最多为13个 2^13也同样到达了1e4的复杂度. 考虑 hash状压 即 2的个数有x个 那么我们就有状态w表示2还有x个. 这样做的原因是把一些相同的东西给合并起来 而并非分散开来.即有多个2直接记录有多少个即可. 可以发现 这样做不同的除数最多只有5个 状态量较

@省选模拟赛03/16 - T3@ 超级树

目录 @description@ @solution@ @accepted code@ @details@ @description@ 一棵 k-超级树(k-SuperTree) 可按如下方法得到:取一棵深度为 k 的满二叉树,对每个节点向它的所有祖先连边(如果这条边不存在的话). 例如,下面是一个 4-超级树: 请统计一棵 k-超级树 中有多少条不同的简单有向路径,对 mod 取模. input 一行两整数 k, mod. output 一行一整数表示答案. example input1: 2