ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

11.1 模拟赛:订正

2021-11-01 22:31:08  阅读:183  来源: 互联网

标签:ch 订正 int sum 11.1 times cdot 模拟 mod


最近几次模拟赛都比较难,一一爆零。要么是完全不知道怎么做,要么是因为对于一道自己认为很有思路的题一直调不出来。不过确实应该首先写好所有的暴力。

进入正题。http://47.92.197.167:5283/contest/115
Author:Kewth

T1 集合均值

image
等价于是随机生成 \(B\) 的排列,求所有前缀的“平均”(当然还要算开头的 \(0\))。那么

\[\sum_{i=1}^{|B|}\left(\dfrac{\sum_{j=1}^ia_i}{i+1}\right)\\ =\sum_{i=1}^{|B|}\left(a_i\times g(i)\right),g(i)=\sum_{j=i+1}^{|B|+1}\dfrac{1}{j} \]

那么由于所有的排列方案我们都会算到,而 \(a_i\) 放在排列中每一个位置 \(x\) 的贡献为 \(g(x)\cdot a_i\cdot (|B|-1)!\),所以由分配律得答案为 \(\sum_{i=1}^{|B|}a_i\times\sum_{i=1}^{|B|} g(i)\times \dfrac{(|B|-1)!}{方案数}\)。
那么有多少种方案呢?显然是 \(|B|!\),因此最终答案为

\[\dfrac{\sum_{i=1}^{|B|}a_i\times\sum_{i=1}^{|B|} g(i)}{|B|} \]

其中 \(g(i)\) 可以由逆元线性推导出,因此复杂度为 \(O(n)\)。
备注:之前一直没有调出来就是因为算贡献的时候没有乘上 \((|B|-1)!\) !

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5, M = 2e7 + 5, mod = 998244353;
int read() {
    char ch = getchar();
    int x = 0, f = 1;
    while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-')
        f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return x * f;
}
int n, m, sum, tot, a[N], jc[M], ijc[M], inv[M];
int qp(int a, int b) {
    int c = 1;
    for (; b; b >>= 1, a = 1ll * a * a % mod)
        if (b & 1)
            c = 1ll * c * a % mod;
    return c;
}
int inver(int b) {
    return qp(b, mod - 2);
}
int frac(int a, int b) { return qp(b, mod - 2) * a % mod; }
signed main() {
    freopen("mos.in", "r", stdin);
    freopen("mos.out", "w", stdout);
    n = read(), m = read();
    jc[0] = ijc[0] = inv[0] = 1;
    for (int i = 1; i <= n; i++) a[i] = read(), tot = (tot + a[i]) % mod;
    for (int i = 1; i <= n * m + 1; i++) jc[i] = 1ll * jc[i - 1] * i - 1ll * jc[i - 1] * i / mod * mod;
    ijc[n * m + 1] = inver(jc[n * m + 1]);
    for (int i = n * m; i; i--) ijc[i] = 1ll * ijc[i + 1] * (i + 1) - 1ll * ijc[i + 1] * (i + 1) / mod * mod;
    for (int i = 1; i <= n * m + 1; i++) inv[i] = 1ll * ijc[i] * jc[i - 1] % mod;
    int s = 0;
    for (int i = n * m + 1; i > 1; i--) s = (inv[i] + s) % mod, sum = (sum + s) % mod;
    cout << 1ll * m * tot % mod * sum % mod * inv[n * m] % mod;
}

T3 技术情报局

image
image
区间?最大值?笛卡尔树! 这就是本题核心。
参见 笛卡尔树 O(n) 建树方法 建立笛卡尔树(大根)。观察到如果我们记录左右子树所代表的区间的“左端点连乘和”&“右端点连乘和”(为了方便叙述而定义的名词,“左端点连乘和”表示 \(fl([l,r])=\sum_{i=l}^r\left(\prod_{j=l}^ia_i\right)\))设树中 \(f(x)\) 表示 \(x\) 所代表区间的 \(\prod\),则可以 O(1) 地更新每一个根的 \(f,fl,fr\),具体见代码。
考虑对于一个根如何统计以它为最大值的区间答案,显然答案加上 \((fr(ls_x)+1)\cdot(fl(rs_x)+1)\cdot a_x^2\)。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e7 + 5;
int n, l, r, s, p, n_, k, top, ans, a[N], ls[N], rs[N], stk[N], f[N], fl[N], fr[N];
namespace GenHelper {
unsigned z1, z2, z3, z4, b;
unsigned rand_() {
    b = ((z1 << 6) ^ z1) >> 13;
    z1 = ((z1 & 4294967294U) << 18) ^ b;
    b = ((z2 << 2) ^ z2) >> 27;
    z2 = ((z2 & 4294967288U) << 2) ^ b;
    b = ((z3 << 13) ^ z3) >> 21;
    z3 = ((z3 & 4294967280U) << 7) ^ b;
    b = ((z4 << 3) ^ z4) >> 12;
    z4 = ((z4 & 4294967168U) << 13) ^ b;
    return (z1 ^ z2 ^ z3 ^ z4);
}
}  // namespace GenHelper
void get(int n, unsigned s, int l, int r) {
    using namespace GenHelper;
    z1 = s;
    z2 = unsigned((~s) ^ 0x233333333U);
    z3 = unsigned(s ^ 0x1234598766U);
    z4 = (~s) + 51;
    for (int i = 1; i <= n; i++) {
        int x = rand_() & 32767;
        int y = rand_() & 32767;
        a[++n_] = (l + (x * 32768 + y) % (r - l + 1));
    }
}
void dfs(int x) {
    f[x] = a[x];
    if (ls[x])
        dfs(ls[x]), f[x] *= f[ls[x]], f[x] %= p;
    if (rs[x])
        dfs(rs[x]), f[x] *= f[rs[x]], f[x] %= p;
    fl[x] = (fl[ls[x]] + (fl[rs[x]] + 1) * f[ls[x]] % p * a[x] % p) % p;
    fr[x] = (fr[rs[x]] + (fr[ls[x]] + 1) * f[rs[x]] % p * a[x] % p) % p;
    ans += (fr[ls[x]] + 1) * (fl[rs[x]] + 1) % p * a[x] % p * a[x] % p, ans %= p;
    // printf("%d: %d %d %d\n",x,f[x],fl[x],fr[x]);
}
signed main() {
    freopen("tio.in", "r", stdin);
    freopen("tio.out", "w", stdout);
    cin >> n >> s >> l >> r >> p;
    get(n, s, l, r);
    for (int i = 1; i <= n; i++) {
        int k = top;
        while (k && a[stk[k]] < a[i]) k--;
        if (k)
            rs[stk[k]] = i;
        if (k < top)
            ls[i] = stk[k + 1];
        stk[++k] = i;
        top = k;
    }
    int id = 0;
    for (int i = 1; i <= n; i++)
        if (a[id] < a[i])
            id = i;
    f[0] = 1;
    dfs(id);
    cout << ans;
}

对我来说这两题难度都不算简单,但在本场比赛中似乎是签到题……

标签:ch,订正,int,sum,11.1,times,cdot,模拟,mod
来源: https://www.cnblogs.com/impyl/p/15494667.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有