• 联赛模拟测试5题解


    T1: 平均数

      分析:这个题昨天考试的时候一看就想到了之前做过两次的一个题型,但是似乎没有办法用两个指针一起扫的方法解决, 感觉平均数这个东西怪怪的没有单调性, 于是n^2暴力加信仰剪枝, 40pts get(信仰剪枝怎么没发挥作用!!!)

      这道题还是有区别的,但是和之前学姐讲过的0/1分数规划有很多的相似之处,二分答案是要的,于是我们重点考虑怎么检验,满足条件的区间一定满足(sum[r] - sum[l - 1]) / (r - l + 1) >= x,于是sum[r] - sum[l - 1] >= x * ( r - l + 1); 变形之后有sum[r] - x * r >= sum[r - 1] - x * (l - 1);发现这个似乎是一个逆序对, 于是直接归并排序 / 树状数组就可以了, 但是本题树状数组常数巨大, 于是采用归并的方式进行实现。时间复杂度为O(N(logN) ^ 2), 学长开的3333ms可以稳过。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 const int N = 1e5 + 10;
     5 double a[N], l, r;
     6 typedef long long ll;
     7 ll x[N], sum[N];
     8 ll n, m, res;
     9 double b[N], c[N];
    10 void merge_sort(int l, int r) {
    11     int mid = (l + r) >> 1;
    12     if(l == r) return;
    13     merge_sort(l, mid);
    14     merge_sort(mid + 1, r);
    15     for(int i = l; i <= r; ++i) {
    16         if(i <= mid) b[i] = a[i];
    17         else c[i] = a[i];
    18     }
    19     int _l = l, _r = mid + 1, head = l - 1;
    20     while(_l <= mid && _r <= r) {
    21         if(b[_l] < c[_r]) {
    22             a[++head] = b[_l];
    23             _l++;
    24         }
    25         else {
    26             a[++head] = c[_r];
    27             res += mid - _l + 1;
    28             _r++;
    29         }
    30     }
    31     while(_l <= mid) {
    32         a[++head] = b[_l++];
    33     }
    34     while(_r <= r) {
    35         a[++head] = c[_r++];
    36     }
    37 }
    38 bool Judge(double pos) {
    39     for(int i = 0; i <= n; ++i)
    40         a[i] = sum[i] - pos * i;
    41     res = 0;
    42     merge_sort(0, n);
    43     return res >= m;
    44 }
    45 int main() {
    46     freopen("ave.in", "r", stdin);
    47     freopen("ave.out", "w", stdout);
    48     scanf("%d%d", &n, &m);
    49     for(int i = 1; i <= n; ++i)  {
    50         scanf("%lld", &x[i]);
    51         sum[i] = sum[i - 1] + x[i];
    52         if(x[i] > r) r = x[i];
    53     }
    54     while(r - l > 1e-7) {
    55         double mid = (l + r) / 2;
    56         if(Judge(mid)) r = mid;
    57         else l = mid;
    58     }
    59     printf("%.4lf
    ", l);
    60     return 0;
    61 }
    View Code

    T2:涂色游戏

      垃圾卡常出题人!!! 垃圾卡常出题人!!! 垃圾卡常出题人!!!

      通过这一道题成功让我引起了对快速模和register的重视。。。

      分析:对于p, q都小于等于5的点, 我们注意到可以利用状压来进行对于状态的记录, 于是定义dp[i][j]表示第i列的使用画笔的状态是j的方案数, 存在方程dp[i][j] += dp[i - 1][k] * calc(count(k), n), 转移的条件是count(j | k) >= q,其中的calc函数表示把count(k)个东西分为n段,没有空段且每个东西都用到的方案数,即第二斯特林数,初始状态都是1, 答案就是把dp[m][1~ed]加起来就可以了,什么?你说你会n^2求calc函数?我都写状压了还在意这些时间? 40pts get

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 const int Mod = 998244353;
     5 typedef long long ll;
     6 const int N = 2100;
     7 int n, m, p, q;
     8 namespace Case1 {
     9     ll dp[1100][1 << 5];
    10     ll fac[N], ny[N], f[N];
    11     void getny(int x) {
    12         fac[0] = 1;
    13         for(int i = 1; i <= x; ++i)
    14             fac[i] = fac[i - 1] * i % Mod;
    15         ny[1] = 1;
    16         for(int i = 2; i <= x; ++i)
    17             ny[i] = (Mod - Mod / i) * ny[Mod % i] % Mod;
    18         f[0] = 1;
    19         for(int i = 1; i <= x; ++i)
    20             f[i] = f[i - 1] * ny[i] % Mod;
    21     }
    22     ll comb(int x, int y) {
    23         return fac[y] * f[x] % Mod * f[y - x] % Mod;
    24     }
    25     int count(int t) {
    26         int res = 0;
    27         while(t) {
    28             if(t & 1) res++;
    29             t >>= 1;
    30         }
    31         return res;
    32     }
    33     ll calc(int x, int y) {
    34         ll res = 0;
    35         if(!x) {
    36             if(!y) return 1;
    37             return 0;
    38         }
    39         if(!y) return 0;
    40         for(int i = 1; i <= y; ++i) {
    41             res = (res + calc(x - 1, y - i) * comb(i, y) % Mod) % Mod;
    42         }
    43         return res;
    44     }
    45     void Solve() {
    46         getny(1100);
    47         int ed = (1 << p) - 1;
    48         for(int i = 1; i <= ed; ++i) {
    49             dp[1][i] = 1;
    50         }
    51         for(int i = 2; i <= m; ++i)
    52             for(int j = 1; j <= ed; ++j) 
    53                 for(int k = 1; k <= ed; ++k){
    54                     if(count(j | k) < q) continue;
    55                     dp[i][j] += dp[i - 1][k] * calc(count(k), n);
    56                     dp[i][j] %= Mod;
    57                 }
    58         ll res = 0;
    59         for(int i = 1; i <= ed; ++i) {
    60             res += dp[m][i] * calc(count(i), n);
    61             res %= Mod;
    62         }
    63         printf("%lld
    ", res);
    64     }
    65 }
    66 int main() {
    67     freopen("color.in", "r", stdin);
    68     freopen("color.out", "w", stdout);
    69     scanf("%d%d%d%d", &n, &m, &p, &q);
    70     Case1::Solve();
    71     return 0;
    72 }
    View Code

      对于每行选出的数字, 发现貌似我们不记录状态也可以求解,若本列选了j个画笔,上一列选了k个画笔,我们计算答案的时候可以再记录一个s表示j和k里面重合的点的个数,于是本层对于答案的贡献就是comb(s, k) * comb(j - s, p - k) * d[j], 第一个comb表示从k个数字分出s个j的方案,第二个comb表示从剩下的p - k个数字里面选出j - s的数字和s个共同数字来拼出j的方案数,d[j]就是前面提到的第二斯特林数,省去了calc里面的第二个数字n,这里就只能n^2递推了,存在d[1] = 1, d[i] = qpow(i, n) - sigema{d[j] * comb(j, i)} (1 <= j < i), 于是我们的dp方程可以定义为第i列涂了j个颜色的方案数,转移:dp[i][j] += dp[i - 1][k] * comb(s, k) * comb(j - s, p - k) * d[j],发现每次dp(i, j)转移时dp(i - 1, k)乘的系数是相同的于是可以提前处理出来,70pts get

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 const int N = 1100 + 10;
     5 const int Mod = 998244353;
     6 typedef long long ll;
     7 ll dp[N][N], d[N], mat[N][N];
     8 int n, m, q, p;
     9 ll qpow(ll x, int cnt) {
    10     ll res = 1;
    11     while(cnt) {
    12         if(cnt & 1) res = (res * x) % Mod;
    13         cnt >>= 1;
    14         x = (x * x) % Mod;
    15     }
    16     return res;
    17 }
    18 ll fac[N], ny[N], f[N];
    19 void getny(int x) {
    20     fac[0] = 1;
    21     for(int i = 1; i <= x; ++i)
    22         fac[i] = fac[i - 1] * i % Mod;
    23     ny[1] = 1;
    24     for(int i = 2; i <= x; ++i)
    25         ny[i] = (Mod - Mod / i) * ny[Mod % i] % Mod;
    26     f[0] = 1;
    27     for(int i = 1; i <= x; ++i)
    28         f[i] = f[i - 1] * ny[i] % Mod;
    29 }
    30 ll comb(int x, int y) {
    31     return fac[y] * f[x] % Mod * f[y - x] % Mod;
    32 }
    33 void getd(int x) {
    34     d[1] = 1;
    35     for(int i = 2; i <= x; ++i) {
    36         d[i] = qpow(i, n);
    37         for(int j = 1; j <= i - 1; ++j){
    38             d[i] = d[i] - 1ll * d[j] * comb(j, i) % Mod;
    39             d[i] = (d[i] % Mod + Mod) % Mod;
    40         }
    41     }
    42 }
    43 signed main() {
    44     freopen("color.in", "r", stdin);
    45     freopen("color.out", "w", stdout);
    46     scanf("%d%d%d%d", &n, &m, &p, &q);
    47     getny(1000);
    48     getd(1000);
    49     for(int i = 1; i <= p; ++i) {
    50         dp[1][i] = 1ll * comb(i, p) * d[i] % Mod;
    51     }
    52     for(int j = 1; j <= p; ++j) {
    53         for(int k = 1; k <= p; ++k) {
    54             int t = std::min(j, k);
    55             for(int s = 0; s <= t; ++s) {
    56                 if(k + j - s > p || k + j - s < q) continue;
    57                 mat[j][k] = (mat[j][k] + comb(s, k) * comb(j - s, p - k) % Mod * d[j] % Mod) % Mod;
    58             }
    59         }
    60     }
    61     for(int i = 2; i <= m; ++i) {
    62         for(int j = 1; j <= p; ++j) {
    63             for(int k = 1; k <= p; ++k) {
    64                 dp[i][j] = (dp[i][j] + dp[i - 1][k] * mat[j][k] % Mod) % Mod;
    65             }
    66         }
    67     }
    68     ll res = 0;
    69     for(int i = 1; i <= p; ++i) {
    70         res = (res + dp[m][i]) % Mod;
    71     }
    72     printf("%lld
    ", res);
    73     return 0;
    74 }
    View Code

      最后我们发现m贼大,可以联想到矩阵乘法,前面也处理出来了没一个dp(i, j)需要运算的系数, 放入矩阵里面就可以加速运算了,由于本题卡常卡的丧心病狂,你的满分算法很有可能会掉到70档,关于卡常:https://www.cnblogs.com/Midoria7/p/13717348.html,另外多用register和快速模, 尽可能少的开longlong进行计算也是个好方法。

    不要问我为什么存在typedef int ll的操作, 卡常的时候我懒的把所有ll改为int了

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 const int N = 105 + 10;
     5 const int Mod = 998244353;
     6 typedef int ll;
     7 #define reg register
     8 void add(ll &x, int y) {
     9     if(x + y >= Mod) x = x + y - Mod;
    10     else x = x + y;
    11 }
    12 ll dp[N][N], d[N], mat[N][N];
    13 int n, m, q, p;
    14 ll qpow(reg ll x, reg int cnt) {
    15     reg ll res = 1;
    16     while(cnt) {
    17         if(cnt & 1) res = (1ll * res * x) % Mod;
    18         cnt >>= 1;
    19         x = (1ll * x * x) % Mod;
    20     }
    21     return res;
    22 }
    23 ll fac[N], ny[N], f[N];
    24 void getny(reg int x) {
    25     fac[0] = 1;
    26     for(reg int i(1); i <= x; ++i)
    27         fac[i] = 1ll * fac[i - 1] * i % Mod;
    28     ny[1] = 1;
    29     for(reg int i(2); i <= x; ++i)
    30         ny[i] = 1ll * (Mod - Mod / i) * ny[Mod % i] % Mod;
    31     f[0] = 1;
    32     for(reg int i(1); i <= x; ++i)
    33         f[i] = 1ll * f[i - 1] * ny[i] % Mod;
    34 }
    35 ll comb(reg int x, reg int y) {
    36     return 1ll * fac[y] * f[x] % Mod * f[y - x] % Mod;
    37 }
    38 void getd(reg int x) {
    39     d[1] = 1;
    40     for(reg int i(2); i <= x; ++i) {
    41         d[i] = qpow(i, n);
    42         for(reg int j(1); j <= i - 1; ++j){
    43             d[i] = d[i] - 1ll * d[j] * comb(j, i) % Mod;
    44             if(d[i] < 0) d[i] = (d[i] % Mod + Mod) % Mod;
    45         }
    46     }
    47 }
    48 struct squ {
    49     ll a[101][101];
    50 } res, now;
    51 squ mul(squ x, squ y) {
    52     squ ans;
    53     memset(ans.a, 0, sizeof(ans.a));
    54     for(reg int i(1); i <= p; ++i)
    55         for(reg int j(1); j <= p; ++j) {
    56             for(reg int k(1); k <= p; ++k) {
    57                 add(ans.a[i][j], 1ll * y.a[i][k] * x.a[k][j] % Mod);
    58             }
    59         }
    60     return ans;
    61 }
    62 int main() {
    63     freopen("color.in", "r", stdin);
    64     freopen("color.out", "w", stdout);
    65     scanf("%d%d%d%d", &n, &m, &p, &q);
    66     getny(1000);
    67     getd(1000);
    68     for(reg int i(1); i <= p; ++i) {
    69         res.a[i][1] = 1ll * comb(i, p) * d[i] % Mod;
    70     }
    71     for(reg int j(1); j <= p; ++j) {
    72         for(reg int k(1); k <= p; ++k) {
    73             reg int t = std::min(j, k);
    74             for(reg int s(0); s <= t; ++s) {
    75                 if(k + j - s > p || k + j - s < q) continue;
    76                 add(now.a[j][k], 1ll * comb(s, k) * comb(j - s, p - k) % Mod * d[j] % Mod);
    77             }
    78         }
    79     }
    80     m--;
    81     while(m) {
    82         if(m & 1) res = mul(res, now);
    83         m >>= 1;
    84         now = mul(now, now);
    85     }
    86     reg ll ans = 0;
    87     for(reg int i(1); i <= p; ++i) {
    88         add(ans, res.a[i][1]);
    89     }
    90     printf("%d
    ", ans);
    91     return 0;
    92 }
    View Code

    T3:序列

     考场上分块没调出来,只交了暴力得了20pts,但是好像G神加上register得了40pts??

      分析:我们发现其实我们发现题目强制在线了修改,没有强制询问,于是我们可以考虑从询问下手,我们可以从线段树优化建图里面得到启发,把每一个询问区间拍到线段树里面,对于每一个线段树上的节点开一个vector,最终答案的统计可以转化为每个点从根节点出发,在到叶子的经过每一个节点的vector里面二分查找小于当前数字的询问区间的x的个数,直接累加答案就行了,对于修改直接把该位置上一次的贡献减去,把新的贡献加上就可以了。时间复杂度O(N(logN) ^ 2),经过卡常可以通过,卡常难度比T2小

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <vector>
     5 #define rint register int
     6 const int N = 1e6 + 10;
     7 std::vector<int> ve[N];
     8 using std::lower_bound;
     9 int read(){
    10     rint s=0,f=1;
    11     char ch=getchar();
    12     while(ch < '0' || ch > '9'){
    13         if(ch=='-') f=-1;
    14         ch=getchar();
    15     }
    16     while(ch >= '0' && ch <= '9'){
    17         s=(s<<1)+(s<<3)+(ch^48);
    18         ch=getchar();
    19     }
    20     return s*f;
    21 }
    22 int a[N];
    23 #define debug puts("-debug-")
    24 #define lson (t << 1)
    25 #define rson (t << 1 | 1)
    26 #define mid ((l + r) >> 1)
    27 void change(rint t, rint l, rint r, rint cl, rint cr, rint num) {
    28     if(cl <= l && r <= cr) {
    29         ve[t].push_back(num);
    30         return;
    31     }
    32     if(cr <= mid) change(lson, l, mid, cl, cr, num);
    33     else if(cl > mid) change(rson, mid + 1, r, cl, cr, num);
    34     else {
    35         change(lson, l, mid, cl, cr, num);
    36         change(rson, mid + 1, r, cl, cr, num);
    37     }
    38 }
    39 int Binary(rint t, rint l, rint r, rint x) { //卡常。。。
    40     while(l <= r) {
    41         if(ve[t][mid] > x) r = mid - 1;
    42         else l = mid + 1;
    43     }
    44     return l;
    45 }
    46 int query(rint t, rint l, rint r, rint pos, rint num) {
    47     rint res = 0;
    48     rint p = Binary(t, 0, ve[t].size() - 1, num);
    49     res += p;
    50     if(l == r) return res;
    51     if(pos <= mid) res += query(lson, l, mid, pos, num);
    52     else res += query(rson, mid + 1, r, pos, num);
    53     return res;
    54 }
    55 struct Q {
    56     int l, r, x;
    57     bool operator < (const Q &a) const {
    58         return a.x > x;
    59     }
    60 } b[N];
    61 signed main() {
    62     freopen("seq.in", "r", stdin);
    63     freopen("seq.out", "w", stdout);
    64     rint n = read(), m = read(), q = read();
    65     for(rint i = 1; i <= n; ++i) {
    66         a[i] = read();
    67     }
    68     for(rint i = 1; i <= m; ++i) {
    69         b[i].l = read();
    70         b[i].r = read();
    71         b[i].x = read();
    72     }
    73     std::sort(b + 1, b + m + 1);
    74     for(rint i = 1; i <= m; ++i) {
    75         change(1, 1, n, b[i].l, b[i].r, b[i].x);
    76     }
    77     rint tot = n * 4 - 1;
    78     for(rint i = 1; i <= tot; ++i) {
    79         std::sort(ve[i].begin(), ve[i].end());
    80     }
    81     rint res = 0;
    82     for(rint i = 1; i <= n; ++i) {
    83         res += query(1, 1, n, i, a[i]);
    84     }
    85     printf("%d
    ", res);
    86     for(rint i = 1; i <= q; ++i) {
    87         rint p = read(), v = read();
    88         rint pre = res;
    89         rint pos = pre ^ p;
    90         res -= query(1, 1, n, pos, a[pos]);
    91         a[pos] = pre ^ v;
    92         res += query(1, 1, n, pos, a[pos]);
    93         printf("%d
    ", res);
    94     }
    95     return 0;
    96 }
    View Code

      熟悉主席树的人可能已经发现了上面的过程可以用主席树进行实现,具体操作和vector的做法相似,但是时间复杂度降到了O(NlogN)不用卡常也可以通过。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 2e5 + 5;
     4 inline int read(int s = 0, int f = 1, char ch = getchar()) {
     5     while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
     6     while(isdigit(ch)) { s = s*10 + ch - '0'; ch = getchar(); }
     7     return s * f;
     8 }
     9 vector<pair<int, int> > qu[N];
    10 int n, m, q, a[N], edit[N], tot;
    11 long long ans;
    12 struct tree {
    13     int ls, rs, val; 
    14 } tr[N * 28];
    15 int modify(int u, int v, int l, int r, int p, int w) {
    16     u = ++tot;
    17     if(l == r) {
    18         tr[u].val = tr[v].val + w;
    19         return u;
    20     }
    21     int mid = (l + r) / 2;
    22     if(p <= mid) tr[u].rs = tr[v].rs, tr[u].ls = modify(tr[u].ls, tr[v].ls, l, mid, p, w);
    23     else tr[u].ls = tr[v].ls, tr[u].rs = modify(tr[u].rs, tr[v].rs, mid + 1, r, p, w);
    24     tr[u].val = tr[tr[u].ls].val + tr[tr[u].rs].val;
    25     return u;
    26 }
    27 int query(int rt, int l, int r, int s, int t) {
    28     if(rt == 0) return 0;
    29     if(s <= l && r <= t) return tr[rt].val;
    30     int mid = (l + r) / 2;
    31     int ans = 0;
    32     if(s <= mid) ans += query(tr[rt].ls, l, mid, s, t);
    33     if(t > mid) ans += query(tr[rt].rs, mid + 1, r, s, t);
    34     return ans;
    35 } 
    36 int main() {
    37     freopen("seq.in", "r", stdin);
    38     freopen("seq.out", "w", stdout);
    39     n = read(), m = read(), q = read();
    40     for(register int i = 1; i <= n; ++i) 
    41         a[i] = read();
    42     for(register int i = 1, l, r, x; i <= m; ++i) {
    43         l = read(), r = read(), x = read();
    44         qu[l].push_back(make_pair(x, 1));
    45         qu[r + 1].push_back(make_pair(x, -1));
    46     }
    47     for(register int i = 1; i <= n; ++i) {
    48         edit[i] = edit[i-1];
    49         for(register int j = 0; j < qu[i].size(); ++j) {
    50             edit[i] = modify(edit[i], edit[i], 0, n, qu[i][j].first, qu[i][j].second);
    51         }
    52         ans += query(edit[i], 0, n, 0, a[i]);
    53     } 
    54     cout << ans << '
    ';
    55     for(register int i = 1, p, v; i <= q; ++i) {
    56         p = read(), v = read();
    57         p ^= ans, v ^= ans;
    58         ans -= query(edit[p], 0, n, 0, a[p]);
    59         ans += query(edit[p], 0, n, 0, v);
    60         a[p] = v;
    61         printf("%lld
    ", ans);
    62     }
    63     return 0;
    64 }
    View Code
  • 相关阅读:
    前端性能优化
    技术从业者的未来(二)
    微服务架构
    SpringCloud 微服务最佳开发实践
    架构师之路
    SpringBoot开发秘籍
    架构设计方法论
    消息架构的设计难题以及应对之道
    SpringCloud 中如何防止绕过网关请求后端服务?
    微服务架构授权是在网关做还是在微服务做?
  • 原文地址:https://www.cnblogs.com/li-jia-hao/p/13720877.html
Copyright © 2020-2023  润新知