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=(kn−2k)×26n−3k
容斥, 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=∑i≥k(−1)i−k(i−ki)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=i≥k∑(−1)i−k(i−ki)Gn,ik!Fn,k=i≥k∑(−1)i−k(i−k)!i!Gn,ik!Fn,k=i≥k∑(i−k)!(−1)i−ki!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[i−k]×c[i]存到k里
把b翻转变成b[n-i+k]
b [ n − i + k ] × c [ i ] b[n-i+k] \times c[i] b[n−i+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∣∣˙Xj∣XiX˙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=1∑n(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;
}