20240218模拟赛
20240218 模拟赛
T1
有一个 \(n\times m\) 的矩阵 \(A_{i,j}\)。\(q\) 次询问 \(x_1,y_1,x_2,y_2,s,t\),求
\[\sum\limits_{i=x_1}^{y_1}\sum\limits_{j=x_2}^{y_2}[s\le A_{i,j}\le t] \]即 \((x_1,y_1)\) 到 \((x_2,y_2)\) 的子矩阵中有多少个数在 \([s,t]\) 范围内。
\(n,m\le300,q\le10^5\),强制在线。
二维分块,设阈值 \(B\),将矩阵分成 \(\frac{nm}{B^2}\) 块。
将矩阵离散化,值域是 \(O(nm)\) 的。设 \(f_{i,j,s}\) 为第 \((i,j)\) 块中 \(\le s\) 的数字个数,可以 \(O(\frac{nm}{B^2}\times nm)\) 出来。再令 \(F(i,j,s)=\sum\limits_{t=1}^jf_{i,t,s}\),即对第二维做前缀和。
然后对于每一行,设 \(v0_{i,j}\) 为第 \(i\) 行的 \(1\sim j\) 块的有序数字集合。同样地,令 \(v1_{i,j}\) 为第 \(j\) 列的 \(1\sim i\) 块的有序数字集合。可以 \(O(\frac{n^2m\log n+nm^2\log m}{B})\) 暴力预处理。
对于一次询问,可以拆成四个形如 \(x_0\in[1,x],y_0\in[1,y],A_{x_0,y_0}\in[s,t]\) 的询问。对于一个询问 \((x,y,s,t)\),设 \((x,y)\) 在 \((p,q)\) 块:
- 对于 \((1,1),(x,y)\) 子矩阵内的所有整块,答案是 \(\sum\limits_{i=1}^{p-1}\sum\limits_{j=1}^{q-1}f_{i,j,t}-f_{i,j,s-1}=\sum\limits_{i=1}^{p-1}F(i,j,t)-F(i,j,s-1)\)。时间复杂度 \(O(\frac{n}{B})\)。
 - 对于子矩阵内的右下角,直接 \(O(B^2)\) 暴力即可。
 - 对于子矩阵的下面部分,对于所有 \(x_0\in[(p-1)B+1,x]\),在 \(v0_{x0,q-1}\) 上二分搜索即可。子矩阵的右边部分同理。时间复杂度 \(O(B\log n)\)。
 
令 \(n=300\),\(B=4\) 时达到最快,但是预处理开销太大。当 \(B=17\) 时较为平衡。
Code
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
const int N = 310, M = 1e5 + 10, B = 17;
bool ST;
inline int read() {
    char ch = getchar();
    int x = 0;
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    return x;
}
int bel[N], L[N], R[N];
int n, m, q, type, lastans, a[N][N];
int ls[N * N], tot;
std::vector<int> vec[2][N][N];
int T[N / B + 1][N / B + 1][N * N];
inline int work(int x, int y, int s, int t) {
    if (!x || !y || s > t) return 0;
    int p = bel[x], q = bel[y], ret = 0;
    if (q > 1)
        for (int i = 1; i < p; i++) ret += T[i][q - 1][t] - T[i][q - 1][s - 1];
    if (q > 1)
        for (int i = L[p]; i <= x; i++)
            ret += std::upper_bound(vec[0][i][q - 1].begin(), vec[0][i][q - 1].end(), t)
                 - std::lower_bound(vec[0][i][q - 1].begin(), vec[0][i][q - 1].end(), s);
    if (p > 1)
        for (int i = L[q]; i <= y; i++)
            ret += std::upper_bound(vec[1][p - 1][i].begin(), vec[1][p - 1][i].end(), t)
                 - std::lower_bound(vec[1][p - 1][i].begin(), vec[1][p - 1][i].end(), s);
    for (int i = L[p]; i <= x; i++)
        for (int j = L[q]; j <= y; j++)
            if (s <= a[i][j] && a[i][j] <= t) ret++;
    return ret;
}
bool ED;
int main() {
    double st = clock();
    freopen("bath.in", "r", stdin);
    freopen("bath.out", "w", stdout);
    std::cerr << (&ED - &ST) / 1024 / 1024 << '\n';
    for (int i = 1; i < N; i++) bel[i] = (i - 1) / B + 1;
    for (int i = 1; i <= N / B; i++) L[i] = (i - 1) * B + 1, R[i] = i * B;
    n = read(), m = read(), q = read(), type = read();
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) ls[++tot] = a[i][j] = read();
    std::sort(ls + 1, ls + 1 + tot);
    tot = std::unique(ls + 1, ls + 1 + tot) - ls - 1;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) a[i][j] = std::lower_bound(ls + 1, ls + 1 + tot, a[i][j]) - ls;
    
    for (int i = 1; i < bel[n]; i++)
        for (int j = 1; j < bel[m]; j++) {
            if (j != 1)
                memcpy(T[i][j], T[i][j - 1], sizeof(int) * (tot + 1));
            for (int x = L[i]; x <= R[i] ; x++)
                for (int y = L[j]; y <= R[j]; y++) T[i][j][a[x][y]]++;
        }
    for (int i = 1; i < bel[n]; i++)
        for (int j = 1; j < bel[m]; j++)
            for (int k = 1; k <= tot; k++) T[i][j][k] += T[i][j][k - 1];
    for (int i = 1; i <= n; i++)
        for (int j = 1; j < bel[m]; j++) {
            std::vector<int> tmp;
            for (int k = L[j]; k <= R[j]; k++) tmp.push_back(a[i][k]);
            std::sort(tmp.begin(), tmp.end());
            int cur1 = 0, cur2 = 0, s0 = (int)tmp.size(), s1 = (int)vec[0][i][j - 1].size();
            while (cur1 < s0 && cur2 < s1) {
                if (tmp[cur1] < vec[0][i][j - 1][cur2])
                    vec[0][i][j].push_back(tmp[cur1]), cur1++;
                else
                    vec[0][i][j].push_back(vec[0][i][j - 1][cur2]), cur2++;
            }
            while (cur1 < s0)
                vec[0][i][j].push_back(tmp[cur1]), cur1++;
            while (cur2 < s1)
                vec[0][i][j].push_back(vec[0][i][j - 1][cur2]), cur2++;
        }
    for (int j = 1; j <= m; j++)
        for (int i = 1; i < bel[n]; i++) {
            std::vector<int> tmp;
            for (int k = L[i]; k <= R[i]; k++) tmp.push_back(a[k][j]);
            std::sort(tmp.begin(), tmp.end());
            int cur1 = 0, cur2 = 0, s0 = (int)tmp.size(), s1 = (int)vec[1][i - 1][j].size();
            while (cur1 < s0 && cur2 < s1) {
                if (tmp[cur1] < vec[1][i - 1][j][cur2])
                    vec[1][i][j].push_back(tmp[cur1]), cur1++;
                else
                    vec[1][i][j].push_back(vec[1][i - 1][j][cur2]), cur2++;
            }
            while (cur1 < s0)
                vec[1][i][j].push_back(tmp[cur1]), cur1++;
            while (cur2 < s1)
                vec[1][i][j].push_back(vec[1][i - 1][j][cur2]), cur2++;
        }
    
    for (int a, b, c, d, s, t; q; q--) {
        a = read(), b = read(), c = read(), d = read();
        s = read(), t = read();
        if (type == 1) {
            a ^= lastans, b ^= lastans, c ^= lastans, d ^= lastans;
            s ^= lastans, t ^= lastans;
            a = (a + n - 1) % n + 1, b = (b + m - 1) % m + 1;
            c = (c + n - 1) % n + 1, d = (d + m - 1) % m + 1;
        }
        if (a > c) std::swap(a, c);
        if (b > d) std::swap(b, d);
        if (s > t) std::swap(s, t);
        s = std::lower_bound(ls + 1, ls + 1 + tot, s) - ls;
        t = std::upper_bound(ls + 1, ls + 1 + tot, t) - ls - 1;
        lastans = work(c, d, s, t) - work(a - 1, d, s, t) - work(c, b - 1, s, t) + work(a - 1, b - 1, s, t);
        printf("%d\n", lastans);
    }
    double ed = clock();
    std::cerr << (int)(ed - st) << '\n';
    return 0;
}
T2
\(q\) 次询问,给出 \(n,m\),求:
\[\sum\limits_{i=1}^n\sum\limits_{j=1}^md(i^2)d^(j^2)d(ij) \]其中 \(d(n)\) 为 \(n\) 的约数个数,对 \(1073741824\) 取模。
\(q\le10^4,1\le n,m\le2\times10^5\)。
拜谢 Antarctic_Cube。
由 \(d(ij)=\sum\limits_{x|i}\sum\limits_{y|j}^n[\gcd(x,y)=1]\),而 \([x=1]=\sum\limits_{d|x}\mu(d)\),得到
令 \(F(n,k)=\sum\limits_{x=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{i=1}^{\left\lfloor\frac{n}{kx}\right\rfloor}d(k^2i^2x^2)\),那么原式就变成了 \(\sum\limits_{k=1}\mu(k)F(n,k)F(m,k)\)。
注意到 \(F(n,k)\) 中 \(ix\le\left\lfloor\frac{n}{k}\right\rfloor\),故
直接算不好算。将 \(F(n,k)\) 与 \(F(n-1,k)\) 做差,得到:
考虑 \(n\gets n+1\) 时,对 \(Ans\) 的影响:
变化量是 \(\sum\limits_{k\mid n}\mu(k)d(n^2)d(\frac{n}{k})F(m,k)\),\(n\gets n-1\) 时减去这个量即可。\(m\gets m\pm1\) 时类似。
线性筛出 \(\mu(x),d(x),d(x^2)\)。将所有询问离线跑莫队即可。莫队的时候要对于所有 \(k\) 维护 \(F(n,k)\) 和 \(F(m,k)\)。
感觉这个东西很有意义啊。
Code
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
const int M = 4e5 + 10, B = 777;
const int mod = 1073741824;
bool mark[M];
int pri[M], cnt, d[M], d0[M], pw[M], tim[M], mu[M];
std::vector<int> divs[M];
inline void Sieve() {
    d[1] = d0[1] = 1, tim[1] = 0, pw[1] = mu[1] = 1;
    for (int i = 2; i < M; i++) mark[i] = true;
    for (int i = 2; i < M; i++) {
        if (mark[i]) {
            d[i] = 2, d0[i] = 3, mu[i] = -1;
            pw[i] = i, tim[i] = 1, pri[++cnt] = i;
        }
        for (int j = 1; j <= cnt; j++) {
            if (i * pri[j] >= M) break;
            mark[i * pri[j]] = false;
            if (i % pri[j]) {
                pw[i * pri[j]] = pri[j];
                tim[i * pri[j]] = 1;
                d[i * pri[j]] = d[i] * d[pri[j]];
                d0[i * pri[j]] = d0[i] * d0[pri[j]];
                mu[i * pri[j]] = -mu[i];
            } else {
                pw[i * pri[j]] = pw[i] * pri[j];
                tim[i * pri[j]] = tim[i] + 1;
                d[i * pri[j]] = d[i / pw[i]] * (tim[i] + 2);
                d0[i * pri[j]] = d0[i / pw[i]] * (2 * tim[i] + 3);
                break;
            }
        }
    }
}
int q, bel[M], ans[M];
struct Node {
    int n, m, id;
    bool operator < (const Node& o) const {
        if (bel[n] == bel[o.n])
            return (bel[n] & 1) ^ (m > o.m);
        return bel[n] < bel[o.n];
    }
} Q[M];
int fn[M], fm[M], A;
int main() {
    double st = clock();
    freopen("math.in", "r", stdin);
    freopen("math.out", "w", stdout);
    Sieve();
    for (int i = 1; i < M; i++)
        for (int j = i; j < M; j += i) divs[j].push_back(i);
    for (int i = 1; i < M; i++) bel[i] = (i - 1) / B + 1;
    scanf("%d", &q);
    for (int i = 1; i <= q; i++)
        scanf("%d%d", &Q[i].n, &Q[i].m), Q[i].id = i;
    std::sort(Q + 1, Q + 1 + q);
    int n = 0, m = 0;
    for (int i = 1; i <= q; i++) {
        while (n < Q[i].n) {
            n++;
            for (int k : divs[n]) {
                (fn[k] += 1ll * d0[n] * d[n / k] % mod) %= mod;
                (A += 1ll * mu[k] * d0[n] % mod * d[n / k] % mod * fm[k] % mod) %= mod;
            }
        }
        while (n > Q[i].n) {
            for (int k : divs[n]) {
                (fn[k] -= 1ll * d0[n] * d[n / k] % mod) %= mod;
                fn[k] = (fn[k] + mod) % mod;
                (A -= 1ll * mu[k] * d0[n] % mod * d[n / k] % mod * fm[k] % mod) %= mod;
                A = (A + mod) % mod;
            }
            n--;
        }
        while (m < Q[i].m) {
            m++;
            for (int k : divs[m]) {
                (fm[k] += 1ll * d0[m] * d[m / k] % mod) %= mod;
                (A += 1ll * mu[k] * d0[m] % mod * d[m / k] % mod * fn[k] % mod) %= mod;
            }
        }
        while (m > Q[i].m) {
            for (int k : divs[m]) {
                (fm[k] -= 1ll * d0[m] * d[m / k] % mod) %= mod;
                fm[k] = (fm[k] + mod) % mod;
                (A -= 1ll * mu[k] * d0[m] % mod * d[m / k] % mod * fn[k] % mod) %= mod;
                A = (A + mod) % mod;
            }
            m--;
        }
        ans[Q[i].id] = (A % mod + mod) % mod;
    }
    for (int i = 1; i <= q; i++) printf("%d\n", ans[i]);
    double ed = clock();
    std::cerr << (int) (ed - st) << '\n';
    return 0;
}
T3
给定 \(n,m,k\),求
\[\sum\limits_{l_1,l_2,\cdots,l_n,r_1,r_2,\cdots,r_n}[\forall i,1\le l_i\le r_i\le m][l_1<l_2<\cdots<l_n][r_1<r_2<\cdots<r_n]\sum\limits_{i=1}^m(\sum\limits_{j=1}^n[i\in[l_j,r_j)])^k \]即对于所有 \(1\le l_i\le r_i\le m,l_1<l_2<\cdots<l_n,r_1<r_2<\cdots<r_n\) 的 \(n\) 个区间 \([l_i,r_i)\) 每个数 \(+1\) 然后计算所有数的 \(k\) 次方和之和。对 \(998244353\) 取模。
\(nm\le10^5,k\le10^9\)。
显然 \(n>m\) 时答案是 \(0\)。当 \(n\le m\) 时,显然有 \(n\le\sqrt{10^5}\)。
设 \(f_{i,x,y}\) 为现在考虑的序列的 \(1\sim i\) 的位置,有 \(x\) 个 \(l_*\) 和 \(y\) 个 \(r_*\) 在 \(\le i\) 的位置。\(g_{i,x,y}\) 为其对应的贡献。主动转移,考虑 \(l_{x+1}\) 和 \(r_{y+1}\) 是否等于 \(i+1\) 即可。时间复杂度 \(O(n^2m)\)。
会炸空间,要用滚动数组。
Code
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
const int M = 1e5 + 10, mod = 998244353;
inline int qpow(int b, int p) {
    int ret = 1;
    for (; p; p >>= 1) {
        if (p & 1)
            ret = 1ll * ret * b % mod;
        b = 1ll * b * b % mod;
    }
    return ret;
}
int n, m, k;
int f[2][433][433], g[2][433][433];
int main() {
    freopen("segment.in", "r", stdin);
    freopen("segment.out", "w", stdout);
    scanf("%d%d%d", &n, &m, &k);
    if (n > m) {
        printf("0\n");
        return 0;
    }
    int cur = 0;
    f[cur][0][0] = 1;
    for (int i = 0; i < m; i++) {
        for (int x = 0; x <= n; x++)
            for (int y = 0; y <= n; y++) f[cur ^ 1][x][y] = g[cur ^ 1][x][y] = 0;
        for (int x = 0; x <= n && x <= i; x++)
            for (int y = 0; y <= x; y++) {
                // x 个左括号,y 个右括号
                if (!f[cur][x][y]) continue;
                (g[cur][x][y] += 1ll * f[cur][x][y] * qpow(x - y, k) % mod) %= mod;
                (f[cur ^ 1][x][y] += f[cur][x][y]) %= mod;
                (g[cur ^ 1][x][y] += g[cur][x][y]) %= mod;
                if (x < n) {
                    (f[cur ^ 1][x + 1][y] += f[cur][x][y]) %= mod;
                    (g[cur ^ 1][x + 1][y] += g[cur][x][y]) %= mod;
                }
                if (y < n) {
                    (f[cur ^ 1][x][y + 1] += f[cur][x][y]) %= mod;
                    (g[cur ^ 1][x][y + 1] += g[cur][x][y]) %= mod;
                }
                if (x < n && y < n) {
                    (f[cur ^ 1][x + 1][y + 1] += f[cur][x][y]) %= mod;
                    (g[cur ^ 1][x + 1][y + 1] += g[cur][x][y]) %= mod;
                }
            }
        cur ^= 1;
    }
        
    printf("%d\n", g[cur][n][n]);
    return 0;
}
                    
                
                
            
        
浙公网安备 33010602011771号