2022牛客暑期多校第二场 I. let fat tension

2022牛客暑期多校第二场 I. let fat tension

题意

给定 \(n,k,d\)\(n\)\(X\)\(Y\) 向量的个数,\(k\)\(X\) 向量的维数,\(d\)\(Y\) 向量的维数。

定义 \(le(i,j) = \frac{X_i\cdot X_j}{|X_i|\cdot|X_j|}\) (实际上就是夹角余弦)

定义 \(Y_i^{new} = \sum_{j=1}^n le(i,j)Y_i\)

计算 \(Y_i^{new}\) 向量。输出 \(i = 1,2,\dots,n\) 的答案。

分析

对于 \(X_i\) 向量两两之间求夹角余弦可以先将 \(X_i\) 向量单位化。

单位化之后,构造一个 \(n \times k\) 的矩阵 \(X\),矩阵的第 \(i\) 行塞上单位化后的 \(X_i\) 向量。

计算 \(XX^T\) 矩阵,其中第 \(i\) 行,第 \(j\) 列会得到向量 \(X_i\)\(X_j\) 的夹角余弦。

再构造一个 \(n \times d\) 的矩阵 \(Y\),矩阵的第 \(i\) 行塞上 \(Y_i\) 向量。

此时根据题意,计算出矩阵 \(XX^TY\),其第 \(i\) 行就是要计算的 \(Y_i^{new}\) 向量。

计算这 \(3\) 个矩阵的乘积如果从左向右依次计算将会是 \(O(n^2k+n^2d)\) 的,然而,如果先算后面两个矩阵乘积的结果,再算第一个与后面两个矩阵乘积结果的乘积,复杂度将会变成 \(O(nkd)\)

代码

#include <cmath>
#include <iomanip>
#include <iostream>
using namespace std;
const int maxn = 1e4 + 10;
const int maxk = 60;
const int maxd = 60;
int n, k, d;
double x[maxn][maxk], y[maxn][maxd], tmp[maxk][maxd], ans[maxn][maxd];
int main() {
    cin >> n >> k >> d;
    for (int i = 0; i < n; i++) {
        double norm = 0;
        for (int j = 0; j < k; j++) {
            cin >> x[i][j];
            norm += x[i][j] * x[i][j];
        }
        for (int j = 0; j < k; j++) {
            x[i][j] /= sqrt(norm);
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < d; j++) {
            cin >> y[i][j];
        }
    }
    for (int p = 0; p < k; p++) {
        for (int q = 0; q < n; q++) {
            for (int r = 0; r < d; r++) {
                tmp[p][r] += x[q][p] * y[q][r];
            }
        }
    }
    for (int p = 0; p < n; p++) {
        for (int q = 0; q < k; q++) {
            for (int r = 0; r < d; r++) {
                ans[p][r] += x[p][q] * tmp[q][r];
            }
        }
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < d; j++) {
            cout << fixed << setprecision(8) << ans[i][j] << ' ';
        }
        cout << '\n';
    }

    return 0;
}
posted @ 2022-07-23 20:58  聆竹听风  阅读(75)  评论(0)    收藏  举报