2022牛客多校二_ E I

本文探讨了如何使用数学技巧解决编程竞赛中的字符串计数问题,通过容斥原理和快速傅里叶变换(FFT)进行高效计算。同时,介绍了如何将矩阵运算应用于数据处理,通过矩阵的规范化和转置来实现特定的字符串加权求和操作。
摘要由CSDN通过智能技术生成

2022牛客多校二

传送门

我是小猫,教我数学
下辈子希望我会数学

E - Falfa with Substring

prob.:

d e f    F n , k = def \,\,F_{n, k} = defFn,k= 长度为n且串中恰好有k个“bit”的串的个数

F n , 0 ,   F n , 1 ,   F n , 2 ,   … F n , n F_{n, 0}, \, F_{n, 1}, \, F_{n, 2}, \, \dots F_{n,n} Fn,0,Fn,1,Fn,2,Fn,n

ideas:

将题给要求的“恰好”的范围扩大为“至少”

$def ,,G_{n, k} = $ 长度为n且串中至少有k个“bit”的串的个数

如果我们不考虑重复的串(不去重), G n , k = ( k n − 2 k ) × 2 6 n − 3 k G_{n, k} = (^{n - 2k}_{k}) \times 26^{n-3k} Gn,k=(kn2k)×26n3k

容斥, F n , k = ∑ i ≥ k ( − 1 ) i − k ( i − k i ) G n , i F_{n,k} = \sum_{i \ge k}(-1)^{i - k}(^{i}_{i-k}) G_{n, i} Fn,k=ik(1)ik(iki)Gn,i

直接做复杂度为 O ( n 2 ) O(n^2) O(n2)

分离系数,把组合数展开
F n , k = ∑ i ≥ k ( − 1 ) i − k ( i − k i ) G n , i k ! F n , k = ∑ i ≥ k ( − 1 ) i − k i ! ( i − k ) ! G n , i k ! F n , k = ∑ i ≥ k ( − 1 ) i − k ( i − k ) ! i ! G n , i F_{n,k} = \sum_{i \ge k}(-1)^{i - k}(^{i}_{i-k}) G_{n, i} \\ k! F_{n,k} = \sum_{i \ge k}(-1)^{i - k}\frac{i!}{(i-k)!} G_{n, i} \\ k! F_{n,k} = \sum_{i \ge k}\frac{(-1)^{i - k}}{(i - k)!}i!G_{n, i} Fn,k=ik(1)ik(iki)Gn,ik!Fn,k=ik(1)ik(ik)!i!Gn,ik!Fn,k=ik(ik)!(1)iki!Gn,i
d e f    A i = i ! F n , i ,    B i = ( − 1 ) i ( i ) ! ,    C i = i ! G n , i def \,\, A_i = i! F_{n,i} ,\,\, B_i = \frac{(-1)^{i}}{(i)!} ,\,\, C_i = i!G_{n, i} defAi=i!Fn,i,Bi=(i)!(1)i,Ci=i!Gn,i

考虑NTT,reverse B, A ′ = B × C A' = B \times C A=B×C

希望 b [ i − k ] × c [ i ] b[i - k] \times c[i] b[ik]×c[i]存到k里

把b翻转变成b[n-i+k]

b [ n − i + k ] × c [ i ] b[n-i+k] \times c[i] b[ni+k]×c[i] -> a’[n+k]

平移回来

A i = A n + i ′ A_i = A' _{n + i} Ai=An+i

code:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const ll mod = 998244353, G = 3, Gi = 332748118;
const ll N = 1e6 + 10;

ll limit = 1, L, r[N << 2];
ll a[N << 2], b[N << 2];

ll qpow(ll _a, ll _b) {
    if (_b <= 0) return 1;
    ll ans = 1;
    while (_b) {
        if (_b & 1) ans = (ans * _a) % mod;
        _b >>= 1;
        _a = (_a * _a) % mod;
    }
    return ans;
}

void ntt(ll *A, ll type) {
    auto swap = [](ll &_a, ll &_b) {
        _a ^= _b, _b ^= _a, _a ^= _b;
    };
    for (ll i = 0; i < limit; i++)
        if (i < r[i]) swap(A[i], A[r[i]]);
    for (ll mid = 1; mid < limit; mid <<= 1) {
        ll Wn = qpow(type == 1 ? G : Gi, (mod - 1) / (mid << 1));
        for (ll j = 0; j < limit; j += (mid << 1)) {
            ll w = 1;
            for (ll k = 0; k < mid; k++, w = (w * Wn) % mod) {
                ll x = A[j + k], y = w * A[j + k + mid] % mod;
                A[j + k] = (x + y) % mod,
                        A[j + k + mid] = (x - y + mod) % mod;
            }
        }
    }
}

void NTT(ll n, ll m) {
    limit = 1;
    L = 0;
    while (limit <= n + m) limit <<= 1, L++;
    for (ll i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (L - 1));
    ntt(a, 1), ntt(b, 1);
    for (ll i = 0; i < limit; i++) a[i] = (a[i] * b[i]) % mod;
    ntt(a, -1);
    ll inv = qpow(limit, mod - 2);
    for (ll i = 0; i <= n + m; i++) a[i] = a[i] * inv % mod;
}

ll fac[N << 1], invfac[N << 1];

ll C(ll n, ll m) {
    if (n < m) return 0;
    ll res = 1;
    res = res * fac[n] % mod;
    res = res * invfac[m] % mod;
    res = res * invfac[n - m] % mod;
    return res;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    ll n;
    cin >> n;

    fac[0] = invfac[0] = 1;
    for (ll i = 1; i < (N << 1); ++i) {
        fac[i] = fac[i - 1] * i % mod;
        invfac[i] = qpow(fac[i], mod - 2);
    }

    for (ll i = 0; i <= n; ++i) {
        b[i] = fac[i] * C(n - 2 * i, i) % mod * qpow(26ll, n - 3 * i) % mod;
        a[i] = ((i & 1) ? mod - 1 : 1ll) * invfac[i] % mod;
    }

    reverse(a, a + n +1);

    NTT(n + 1, n + 1);

    for (ll i = 0; i <= n; ++i) {
        ll tmp = a[n + i] * invfac[i] % mod;
        cout << tmp << " ";
    }

    return 0;
}

I - let fat tension

prob.:

已知 X n × k X_{n\times k} Xn×k Y n × d Y_{n\times d} Yn×d l e ( i , j ) = X i X ˙ j ∣ X i ∣ ∣ ˙ X j ∣ le(i,j) = \frac{X_i \dot X_j}{|X_i| \dot |X_j|} le(i,j)=Xi˙XjXiX˙j, 求 Y i n e w = ∑ j = 1 n ( l e ( i , j ) × Y j ) Y_i^{new} = \sum_{j = 1}^{n}( le(i, j) \times Y_j) Yinew=j=1n(le(i,j)×Yj)

ideas:

将操作转化为矩阵形式
Y i n e w = ∑ j = 1 n ( l e ( i , j ) × Y j ) Y n e w = l e × Y l e = X n o r m × X n o r m T Y n e w = X n o r m × X n o r m T × Y Y_i^{new} = \sum_{j = 1}^{n}( le(i, j) \times Y_j) \\ Y^{new} =le \times Y \\ le = X_{norm} \times X_{norm}^{T} \\ Y^{new} = X_{norm} \times X_{norm}^{T} \times Y \\ Yinew=j=1n(le(i,j)×Yj)Ynew=le×Yle=Xnorm×XnormTYnew=Xnorm×XnormT×Y
直接算复杂度会超,根据矩阵的结合律
Y n e w = X n o r m × ( X n o r m T × Y ) Y^{new} = X_{norm} \times (X_{norm}^{T} \times Y) Ynew=Xnorm×(XnormT×Y)
code:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

struct Matrix {
    ll n, m;
//    double a[N][N];
    vector<vector<double> > a;

    void set(ll _a, ll _b) {
        n = _a, m = _b;
        a.resize(n + 5);
        for (int i = 0; i < n + 5; ++i) {
            a[i].resize(m + 5);
        }
//        this.a.resize(n, vector<int>(m));
    }

    Matrix() {
        clear();
    }

    void clear() {
        n = m = 0;
        a.clear();
//        memset(a, 0, sizeof(a));
    }

    Matrix operator+(const Matrix &b) const {
        Matrix tmp;
        tmp.set(n, m);
        for (ll i = 0; i < n; ++i)
            for (ll j = 0; j < m; ++j)
                tmp.a[i][j] = (a[i][j] + b.a[i][j]);
        return tmp;
    }

    Matrix operator-(const Matrix &b) const {
        Matrix tmp;
        tmp.set(n, m);
        for (ll i = 0; i < n; ++i) {
            for (ll j = 0; j < m; ++j)
                tmp.a[i][j] = (a[i][j] - b.a[i][j]);
        }

        return tmp;
    }

    Matrix operator*(const Matrix &b) const {
        Matrix tmp;
        tmp.clear();
        tmp.set(n, b.m);
        for (ll i = 0; i < n; ++i)
            for (ll j = 0; j < b.m; ++j)
                for (ll k = 0; k < m; ++k) {
                    tmp.a[i][j] += a[i][k] * b.a[k][j];
                }
        return tmp;
    }

    Matrix norm() {
        Matrix tmp;
        tmp.set(n, m);
        for (int i = 0; i < n; ++i) {
            double sum = 0;
            for (int j = 0; j < m; ++j) {
                sum += a[i][j] * a[i][j];
            }
            for (int j = 0; j < m; ++j) {
                tmp.a[i][j] = sqrt(a[i][j] * a[i][j] / sum);
            }
        }
        return tmp;
    }


    Matrix transposition() {
        Matrix tmp;
        tmp.set(m, n);
        for (int i = 0; i < tmp.n; ++i) {
            for (int j = 0; j < tmp.m; ++j) {
                tmp.a[i][j] = a[j][i];
            }
        }
        return tmp;
    }

    void print() {
        for(int i = 0; i < n; ++i) {
            for(int j = 0; j < m; ++ j) {
                cerr << a[i][j] << " ";
            }
            cerr << endl;
        }
    }
};

Matrix X, Y;
Matrix Xnorm, XnormT;
Matrix tmp, ans;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int n, k, d;
    cin >> n >> k >> d;
//    X.n = n, X.m = k;
    X.set(n, k);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < k; ++j) {
            cin >> X.a[i][j];
        }
    }

//    Y.n = n, Y.m = d;
    Y.set(n, d);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < d; ++j) {
            cin >> Y.a[i][j];
        }
    }

    Xnorm.set(n, k);
    Xnorm = X.norm();
    XnormT.set(k, n);
    XnormT = Xnorm.transposition();

    tmp.set(k, d);
    tmp = XnormT * Y;
    ans.set(n, d);
    ans = Xnorm * tmp;

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < d; ++j) {
            cout << fixed << setprecision(8) <<  ans.a[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值