CF tracker
Edu List
https://codeforces.com/contest/1644
F. Basis F(a数组,k数字) = 把a每个数组重复k次, 然后取 和a等长的前缀 [a_0_1,a_0_2...,a_0_k, a_1_1...a_1,k, a_2_1,...,a_2_k,...]
`x!=y, G(a数组,x数字,y数字) = 把a中所有x变成y, 所有y变成x
如果满足以下条件,则称a数组是b数组的父数组:
存在 k使得F(a,k) = b
或者 存在x!=y, G(a,x,y) = b
如果 有父数组链关系, 则称作祖先
问题, 给定n,k
构造系列 数组s={ s1,s2,..,sm}
每个si包含n个元素, si中的的值\in [1,k]
对于 任意的长n 值在 [1,k]的数组a, 至少在你给的s中存在一个si是a的祖先
问s的最小长度 mod 998244353
范围 n,k 2e5
6s
512mb
我的思路 si 通过 F,G的操作能变成任意数组
一个纯数字的数组, 那么通过F操作一定是自己的祖先,
两个不同的纯数字数组, 通过一次G操作 互为祖先
纯数字数组, 通过两次操作G一定是自己的祖先,
每个数组通过巨大k 操作, 都能变成纯数字数组
G的性质, 让数组的内容和具体值无关, 变成的一段一段的同样的值,(可以看成交换)
所以 比如 xxxyyyxxzz 是否能达到, 就是看 111221133 是否能达到
F的性质, 则是说不要考虑任何 x * w y * w z * w
的形状, 也就是能找到一个连续段的分割, 因为它总对应一个更小的
如果 k >= n, 那么就好了 [1,2,3,…,n], 啥都能变
–
考虑 k < n
两个没有 前缀 循环节的如何判断是否可以相互转化呢
1 2 3 n=5,k=4 1,2,3,3,4 1,2,2,2,3
一个猜想, 没有用完k的一定可以由 用完k的转化来, 因为至少有同色的个数 >= 2, 而且它不能转化回去(所以它的父一旦能被表示它则一定被表示, 所以它自身不需要存在)
两个序列之间如果可以转化 则 一定没有F操作, 否则形状会变有周期的,
而两个序列如果之间有G操作, 则不满足 从小到达命名1,2,3,4 的性质
综上, 需要统计
长度n, 用完k个,没有循环段, 且满足顺序赋值的个数(每个位置的值 <= 之前用的最大值+1)
有循环段很好算,枚举循环长度就行, 所以就找出满足 顺序赋值 的个数 再来减法
f[i][t] =
前i个最大命名为t的方案数
f[i][t] = f[i-1][t] * t + f[i-1][t-1] * 1
, 分别是用前面一样的和用更大的
那么ans = f[n][k] - 有循环段的
直接搞是 O(n^2)
看成生成方程
$f_i(x) = f_{i-1}’(x) x + x f_{i-1}(x)$
$f_i(x)e^x = x (f_{i-1}e^x)’$
$令a_i = f_i(x)e^x$
再看 系数 $a[i][t] = a[i-1][t] * t$
所以 $a[n][t] = a[0][t] * t^n$
然后乘上$f[n] = a[n] * e^{-x}$ 就能得到 f[n][k]
完了?
$a[0] = f[0] * e^x$
似乎有点问题
题解 显然F(G(a,x,y),m)=G(F(a,m),x,y),所以可以考虑先F再G
G(G(a,x,y),x,y)=a, 所以G的操作都可以恢复, 考虑所有F结束后用G调整顺序
实际是对{1,…,n} 做子集分割
那就是 直接 $\sum \limits_{i=1}^{\min(n, k)} S(n, i)$, 第二类 斯特林数S(n,m) 把n个有差别数 放进m个无差别盒子, 且每个盒子至少一个的方案数
对单个(一行固定n)第二类斯特林数的计算
$S(n, k)=\frac{1}{k!} \sum \limits_{i = 0}^{k} (-1)^i binom{k}{i} (k-i)^n$
$= \sum \limits_{i=0}^{k} \frac{(-1)^i \cdot k! \cdot (k-i)^n}{k! \cdot i! \cdot (k-i)!}$
是 $\frac{(-1)^i}{i!}x^i$ 与 $\frac{i^n}{i!}$ 的卷积
但实际上 当固定n以后, 算多个k 可以前缀一下暴力算, 不需要用ntt
令$A_i = \sum \limits_{j=1}^{\min(i, k)} S(i, j)$
一样的,就是有一段一段等长的同等数字的
然后就是容斥
$\sum \limits_{i=1}^{n} \mu(i) B_i = \sum \limits_{i=1}^{n} \mu(i) A_{\lceil \frac{n}{i} \rceil}$
这里有问题
比如n=3,n=2
1 2 3 A(3) = 4(111,122,121,112) A(2) = 2(11,12) => (111,112) A(1) = 1(1) => (111)
直接上面的容斥会 得到 4-2-1 = 1, 其中 111被重复减掉了
而出问题的就是这种 全部都一样的, (如果不是全部一样, 至少有个 < k的长度的因子)
而全部一样的只有1种, 所以需要A(i)-S(i,1)
代码 https://codeforces.com/contest/1644/submission/189829751
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 #include <bits/stdc++.h> namespace CMM{ const int _mod = 998244353 ; class modint { private : long long _v; public : modint () :_v(0 ) { } modint (long long _a) { _v = (_a < 0 )? _mod - ((-_a) % _mod) : (_a % _mod); } int val () const { return _v; } modint operator +()const { return *this ; } modint operator -()const { return { _mod - _v }; } modint operator +(const modint& rhs) const { return modint (*this ) += rhs; } modint operator -(const modint& rhs) const { return modint (*this ) -= rhs; } modint operator *(const modint& rhs) const { return modint (*this ) *= rhs; } modint operator /(const modint& rhs) const { return modint (*this ) /= rhs; } bool operator ==(const modint& rhs)const { return _v == rhs._v; } bool operator !=(const modint& rhs)const { return _v != rhs._v; } bool operator > (const modint& rhs)const { return _v > rhs._v; } bool operator >=(const modint& rhs)const { return _v >= rhs._v; } bool operator <=(const modint& rhs)const { return _v <= rhs._v; } bool operator < (const modint& rhs)const { return _v < rhs._v; } modint& operator +=(const modint& rhs) { (_v += rhs._v) %= _mod; return *this ; } modint& operator -=(const modint& rhs) { (_v += _mod - rhs._v) %= _mod; return *this ; } modint& operator *=(const modint& rhs) { _v = _v * rhs.val () % _mod; return *this ; } modint& operator /=(const modint& rhs) { _v = _v * rhs.inv ().val () % _mod ; return *this ; } modint pow (long long pwr) const { long long res (1 ) ; long long _b(_v); while (pwr) { if (pwr & 1 ) (res *= _b) %= _mod; (_b *= _b) %= _mod; pwr /= 2 ; } return res; } modint inv () const { assert (_v != 0 ); return pow (_mod - 2 ); } }; }; template <class T > class Binomial { public : std::vector<T> fac; std::vector<T> ifac; std::vector<T> inv; Binomial (){}; void init (int n) { fac.resize (n+1 ,1 ); ifac.resize (n+1 ); inv.resize (n+1 ); for (int i=1 ;i<=n;i++) fac[i]=fac[i-1 ]*i; ifac[n]=fac[n].inv (); for (int i=n-1 ;i>=0 ;i--) ifac[i]=ifac[i+1 ]*(i+1 ); for (int i=1 ;i<=n;i++) inv[i]=fac[i-1 ]*ifac[i]; } Binomial (int n){ init (n); } }; namespace NTT998{ const int MOD = 998244353 ; const int MAXPWR = 22 ; const int g = 3 ; const int invg = 332748118 ; int rev (int x, int len) { int ans = 0 ; while (len -- ){ ans <<= 1 ; ans |= x & 1 ; x >>= 1 ; } return ans; } inline int getlog2 (int n) { return 31 - __builtin_clz(n);} template <class T> T mypow (T a, long long k) { T res = 1 ; while (k) { if (k & 1 ) (res *= a); (a *= a); k >>= 1 ; } return res; } template <class mint> void NTT (std::vector<mint> &A, int flag = 1 ) { int n = A.size (); if (n == 1 ) return ; int lgn = getlog2 (n); for (int i=0 ;i<n;i++) { int j = rev (i, lgn); if (j > i) std::swap (A[i], A[j]); } for (int pwr=0 ;pwr<lgn;pwr++){ int m = 1 << pwr; mint gn = mypow <mint>(flag == 1 ? g : invg, (MOD - 1 ) / (m << 1 )); for (int k = 0 ; k < n; k += (m<<1 )) { mint gi = 1 ; for (int j=0 ;j<m;j++){ mint U = A[k + j]; mint T = A[k + j + m] * gi; A[k + j] = (U + T); A[k + j + m] = (U - T); gi *= gn; } } } if (flag == -1 ){ const mint INVSIZE = mint (n).inv (); for (int i=0 ;i<n;i++) (A[i] *= INVSIZE) ; } } template <class T> void INTT (std::vector<T> &A) { NTT <T>(A,-1 );} template <class T> std::vector<T> convolution (std::vector<T> v0, std::vector<T> v1) { int sz = v0.size () + v1.size (); if (sz == 0 )return {}; sz = 1 << (getlog2 (sz) + !!(sz & (sz-1 ))); v0.resize (sz,0 ); v1.resize (sz,0 ); NTT <T>(v0); NTT <T>(v1); std::vector<T> v2 (sz,0 ) ; for (int i=0 ;i<sz;i++) v2[i] = v0[i] * v1[i]; INTT <T>(v2); return v2; } template <class T> std::vector<T> poly_sq (std::vector<T> v0) { int sz = v0.size () * 2 ; if (sz == 0 )return {}; sz = 1 << (getlog2 (sz) + !!(sz & (sz-1 ))); v0.resize (sz,0 ); NTT <T>(v0); std::vector<T> v2 (sz,0 ) ; for (int i=0 ;i<sz;i++) v2[i] = v0[i] * v0[i]; INTT <T>(v2); return v2; } } typedef long long ll;#define rep(i,a,n) for (ll i=a;i<(ll)n;i++) ll read () {ll r;scanf ("%lld" ,&r);return r;}using mint = CMM::modint;Binomial<mint> b; std::vector<mint> p; int k;mint A (int n) { std::vector<mint> pp; rep (i,0 ,std::min (n,k)+1 ) pp.push_back (p[i]); std::vector<mint> q={0 }; rep (i,1 ,std::min (n,k)+1 ) q.push_back (NTT998::mypow <mint>(i,n)*b.ifac[i]); std::vector<mint> s=NTT998::convolution (pp,q); mint r=0 ; rep (i,1 ,std::min ((int )n,k)+1 ) r+=s[i]; return r; } int main () { int n=read (); k=read (); if (n == 1 || k == 1 ) return 0 * printf ("1\n" ); b.init (n); p.push_back (1 ); rep (i,1 ,std::min (n,k)+1 ) p.push_back (p.back ()*b.inv[i]*(-1 )); mint ans=0 ; std::vector<int > mu (n+1 ,1 ) ; std::vector<bool > prime (n+1 ,1 ) ; rep (i,2 ,n+1 ) if (prime[i]){ for (ll j=i*i;j<=n;j+=i) prime[j] = false ; rep (t,1 ,n+1 ) { if (i*t > n) break ; if (t%i==0 ) mu[i*t] = 0 ; else mu[i*t]*=-1 ; } } rep (len,1 ,n+1 ) if (mu[len]) ans+=(A (n/len + !!(n%len)) - 1 )*mu[len]; printf ("%d\n" , ans.val ()); return 0 ; }
总结 F
一个是想的问题 233 是没有变法变成222的, 只能322, 这点都想错了, 后面当然就错了, 不过大体的思路路线是对的
然后是最后这个- S(i,1)
, 就感觉也卡了
然后是n==1 || k==1的特殊情况
然后这里”卷积”的部分也可以通过交换积分顺序 暴力搞掉
A也可以加cache
参考 官方