给你两个正整数 a , b a,b a,b,求 a × b a \times b a×b。
1 ≤ a , b ≤ 1 0 6 1 \le a, b \le 10^6 1≤a,b≤106
#include
using namespace std;
const int N = 3000010;
typedef long long ll;
const ll mod = 998244353, g = 3;
char s1[N], s2[N];
ll a[N], b[N];
int tot, bit, rev[N];
ll mod_pow(ll x, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
void ntt(ll a[], int inv)
{
for(int i = 0; i < tot; i++)
{
if(i < rev[i]) swap(a[i], a[rev[i]]);
}
for(int mid = 1; mid < tot; mid <<= 1)
{
ll w1 = mod_pow(g, (mod - 1) / (2 * mid));
if(inv == -1) w1 = mod_pow(w1, mod - 2);
for(int i = 0; i < tot; i += 2 * mid)
{
ll wk = 1;
for(int j = 0; j < mid; j++, wk = wk * w1 % mod)
{
ll x = a[i + j], y = wk * a[i + j + mid] % mod;
a[i + j] = (x + y) % mod, a[i + j + mid] = (x - y + mod) % mod;
}
}
}
}
int main()
{
scanf("%s%s", s1, s2);
int n = strlen(s1) - 1, m = strlen(s2) - 1;
for(int i = 0; i <= n; i++) a[i] = s1[n - i] - '0';
for(int i = 0; i <= m; i++) b[i] = s2[m - i] - '0';
while((1 << bit) < n + m + 1) bit++;
tot = 1 << bit;
for(int i = 0; i < tot; i++)
{
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
}
ntt(a, 1), ntt(b, 1);
for(int i = 0; i < tot; i++) a[i] = a[i] * b[i] % mod;
ntt(a, -1);
ll inv = mod_pow(tot, mod - 2);
for(int i = 0; i <= n + m; i++) a[i] = a[i] * inv % mod;
ll tmp = 0;
int id;
for(id = 0;; id++)
{
if(id >= tot && !tmp) break;
tmp += a[id], a[id] = tmp % 10;
tmp /= 10;
}
while(id && a[id] == 0) id--;
for(int i = id; i >= 0; i--) printf("%lld", a[i]);
return 0;
}
给 n n n 个数字 q 1 , q 2 , . . . , q n q_1,q_2,...,q_n q1,q2,...,qn,定义
F j = ∑ i = 1 j − 1 q i × q j ( i − j ) 2 − ∑ i = j + 1 n q i × q j ( i − j ) 2 E i = F i q i F_j = \sum\limits_{i=1}^{j-1}\frac{q_i \times q_j}{(i-j)^2} - \sum\limits_{i = j + 1}^{n}\frac{q_i \times q_j}{(i-j)^2}\\ E_i = \frac{F_i}{q_i} Fj=i=1∑j−1(i−j)2qi×qj−i=j+1∑n(i−j)2qi×qjEi=qiFi
求 E 1 , E 2 , . . . , E n E_1,E_2,...,E_n E1,E2,...,En.
设 f [ i ] = q i , g [ i ] = 1 i 2 f[i]=q_i,g[i]=\frac{1}{i^2} f[i]=qi,g[i]=i21 ,所以原式变成
∑ i = 1 j f [ i ] ∗ g [ j − i ] − ∑ i = j n f [ i ] ∗ g [ i − j ] \sum_{i=1}^{j}f[i]*g[j-i]-\sum_{i=j}^{n}f[i]*g[i-j] i=1∑jf[i]∗g[j−i]−i=j∑nf[i]∗g[i−j]
令 f [ 0 ] = 0 , g [ 0 ] = 0 f[0]=0,g[0]=0 f[0]=0,g[0]=0,则原式变成
∑ i = 0 j f [ i ] ∗ g [ j − i ] − ∑ i = j n f [ i ] ∗ g [ i − j ] \sum_{i=0}^{j}f[i]*g[j-i]-\sum_{i=j}^{n}f[i]*g[i-j] i=0∑jf[i]∗g[j−i]−i=j∑nf[i]∗g[i−j]
这时我们发现,左边已经是一个卷积的形式,所以我们直接来推右边
将 ∑ i = j n f [ i ] ∗ g [ i − j ] \sum_{i=j}^{n}f[i]*g[i-j] ∑i=jnf[i]∗g[i−j] 展开,发现: f [ j ] ∗ g [ 0 ] + f [ j + 1 ] ∗ g [ 1 ] + . . . + f [ j + ( n − j ) ] ∗ g [ n − j ] f[j]*g[0]+f[j+1]*g[1]+...+f[j+(n-j)]*g[n-j] f[j]∗g[0]+f[j+1]∗g[1]+...+f[j+(n−j)]∗g[n−j]
所以我们可以将原式写成: ∑ i = 0 n − j f [ j + i ] ∗ g [ i ] \sum_{i=0}^{n-j}f[j+i]*g[i] i=0∑n−jf[j+i]∗g[i]
引入 f ′ [ i ] = f [ n − i ] f'[i]=f[n-i] f′[i]=f[n−i] ,实际上这是一种翻转的套路。则原式可写为: ∑ i = 0 n − j f ′ [ n − ( j + i ) ] ∗ g [ i ] \sum_{i=0}^{n-j}f'[n-(j+i)]*g[i] i=0∑n−jf′[n−(j+i)]∗g[i]
∑ i = 0 n − j f ′ [ n − j − i ] ∗ g [ i ] \sum_{i=0}^{n-j}f'[n-j-i]*g[i] i=0∑n−jf′[n−j−i]∗g[i]
令 t = n − j t = n-j t=n−j,则原式等于
∑ i = 0 t f ′ [ t − i ] ∗ g [ i ] \sum_{i=0}^{t}f'[t-i]*g[i] i=0∑tf′[t−i]∗g[i]
E j = ∑ i = 0 j f [ i ] ∗ g [ j − i ] − ∑ i = 0 t f ′ [ t − i ] ∗ g [ i ] E_j=\sum_{i=0}^{j}f[i]*g[j-i]-\sum_{i=0}^{t}f'[t-i]*g[i] Ej=i=0∑jf[i]∗g[j−i]−i=0∑tf′[t−i]∗g[i]
#include
using namespace std;
const int N = 400010;
const double PI = acos(-1);
struct Complex
{
double x, y;
Complex(double x_, double y_) : x(x_), y(y_){}
Complex(){}
Complex operator + (Complex& c)
{
return Complex(x + c.x, y + c.y);
}
Complex operator - (Complex& c)
{
return Complex(x - c.x, y - c.y);
}
Complex operator * (Complex& c)
{
return Complex(x * c.x - y * c.y, x * c.y + y * c.x);
}
};
Complex a[N], b[N], c[N];
int tot, bit, rev[N];
void fft(Complex a[], int inv)
{
for(int i = 0; i < tot; i++)
{
if(i < rev[i]) swap(a[i], a[rev[i]]);
}
for(int mid = 1; mid < tot; mid <<= 1)
{
Complex w1(cos(PI / mid), inv * sin(PI / mid));
for(int i = 0; i < tot; i += mid * 2)
{
Complex wk(1, 0);
for(int j = 0; j < mid; j++, wk = wk * w1)
{
auto x = a[i + j], y = wk * a[i + j + mid];
a[i + j] = x + y, a[i + j + mid] = x - y;
}
}
}
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%lf", &a[i].x);
b[i].x = 1.0 / i / i;
}
for(int i = 0; i <= n; i++) c[i].x = a[n - i].x;
while((1 << bit) <= n + n) bit++;
tot = 1 << bit;
for(int i = 0; i < tot; i++)
{
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
}
fft(a, 1), fft(b, 1), fft(c, 1);
for(int i = 0; i < tot; i++)
{
a[i] = a[i] * b[i], c[i] = c[i] * b[i];
}
fft(a, -1), fft(c, -1);
for(int i = 1; i <= n; i++)
{
printf("%.5f\n", (a[i].x - c[n - i].x) / tot);
}
return 0;
}
给两个序列 1 ≤ n ≤ 50000 , 1 ≤ a i , b i ≤ 100 1 \le n \le 50000, 1 \le a_i,b_i \le 100 1≤n≤50000,1≤ai,bi≤100,可以对其中一个序列的所有元素加上一个值 c c c ( c c c 可以是负数),然后把其中一个序列想象成一个环,可以旋转。求 ∑ i = 1 n ( a i − b i ) 2 \sum\limits_{i = 1}^n(a_i - b_i)^2 i=1∑n(ai−bi)2 的最小值.
我们令增加量为 x x x,旋转以后的原数列为 { a } { b } \{a\}\{b\} {a}{b}那么现在的费用就是:
∑ i = 1 n ( a i + x − b i ) 2 \sum_{i=1}^n\left(a_i+x-b_i\right)^2 ∑i=1n(ai+x−bi)2
我们把第i项拿出来拆开,得到:
( a i + x − b i ) 2 = a i 2 + b i 2 + x 2 + 2 a i x − 2 a i b i − 2 b i x \left(a_i+x-b_i\right)^2=a_i^2+b_i^2+x^2+2a_ix-2a_ib_i-2b_ix (ai+x−bi)2=ai2+bi2+x2+2aix−2aibi−2bix
∑ i = 1 n a i 2 + ∑ i = 1 n b i 2 + n x 2 + 2 x ( ∑ i = 1 n a i − ∑ i = 1 n b i ) − 2 ∑ i = 1 n a i b i \sum_{i=1}^na_i^2+\sum_{i=1}^nb_i^2+nx^2+2x\left(\sum_{i=1}^na_i-\sum_{i=1}^nb_i\right)-2\sum_{i=1}^na_ib_i ∑i=1nai2+∑i=1nbi2+nx2+2x(∑i=1nai−∑i=1nbi)−2∑i=1naibi
那么我们只要令最后一项最大,那么就可以得到最小的费用值了
现在问题转化为求 ∑ i = 1 n a i b i \sum_{i=1}^na_ib_i ∑i=1naibi的最大值
我们把数列 { a } \{a\} {a}反过来,变成
∑ i = 1 n a n − i + 1 b i \sum_{i=1}^na_{n-i+1}b_i ∑i=1nan−i+1bi
所以把反过来的数列 { a } \{a\} {a}倍长,和数列 { b } \{b\} {b}卷积,得到的项里面的第 n + 1 n+1 n+1到 n ∗ 2 n*2 n∗2 项的最大值,就是 ∑ i = 1 n a i b i \sum_{i=1}^na_ib_i ∑i=1naibi的最大值. 然后把前面的不变项加上,就是答案了.
#include
using namespace std;
const int N =300010;
typedef long long ll;
const ll mod = 998244353, g = 3;
ll mod_pow(ll x, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
ll a[N], b[N];
int tot, bit, rev[N];
void ntt(ll a[], int inv)
{
for(int i = 0; i < tot; i++)
{
if(i < rev[i]) swap(a[i], a[rev[i]]);
}
for(int mid = 1; mid < tot; mid <<= 1)
{
ll w1 = mod_pow(g, (mod - 1) / (2 * mid));
if(inv == -1) w1 = mod_pow(w1, mod - 2);
for(int i = 0; i < tot; i += 2 * mid)
{
ll wk = 1;
for(int j = 0; j < mid; j++, wk = wk * w1 % mod)
{
ll x = a[i + j], y = wk * a[i + j + mid] % mod;
a[i + j] = (x + y) % mod, a[i + j + mid] = (x - y + mod) % mod;
}
}
}
}
ll A, B, C;
ll f(ll x)
{
return A * x * x + B * x + C;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
C += a[i] * a[i];
B += a[i];
a[n + i] = a[i];
}
for(int i = 1; i <= n; i++)
{
scanf("%lld", &b[i]);
C += b[i] * b[i];
B -= b[i];
}
reverse(b + 1, b + n + 1);
while((1 << bit) <= 3 * n) bit++;
tot = 1 << bit;
for(int i = 0; i < tot; i++)
{
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
}
ntt(a, 1), ntt(b, 1);
for(int i = 0; i < tot; i++)
{
a[i] = a[i] * b[i] % mod;
}
ntt(a, -1);
ll res = 0, inv = mod_pow(tot, mod - 2);
for(int i = n + 1; i <= 2 * n; i++)
{
res = max(res, a[i] * inv % mod);
}
A = n, B *= 2, C -= 2 * res;
//二次函数求最值一定要小心,因为负数的向上取整是/ ,而正数的向下取整是/ ,因此要把这对称轴附近的三个值都取一遍.
ll ans = min(f(-B / (2 * A) - 1), f(-B / (2 * A)));
ans = min(ans, f(-B / (2 * A) + 1));
printf("%lld\n", ans);
return 0;
}
给定一个长为 n n n 的序列 a a a,求出其 k k k 阶差分或前缀和。 1 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 9 , k ≤ 1 0 2333 1 \le n \le 10^5, 1 \le a_i \le 10^9, k \le 10^{2333} 1≤n≤105,1≤ai≤109,k≤102333.
结果的每一项都需要对 1004535809 1004535809 1004535809 取模。
可以看作 g = f ∗ a g = f * a g=f∗a,其中 a a a 是输入的数组, f i = { C k + i − 1 k , t = 0 C k i − 1 , t = 1 f_i = \begin{cases}C_{k+i-1}^k, & t = 0 \\ C_k^{i - 1}, & t = 1\end{cases} fi={Ck+i−1k,Cki−1,t=0t=1
第 i i i 项答案就是卷积的第 i + 1 i + 1 i+1 项的系数.
#include
using namespace std;
typedef long long ll;
const ll mod = 1004535809, g = 3;
const int N = 300010;
ll mod_pow(ll x, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
char str[N];
int tot, bit, rev[N];
ll a[N], f[N];
void ntt(ll a[], int inv)
{
for(int i = 0; i < tot; i++)
{
if(i < rev[i]) swap(a[i], a[rev[i]]);
}
for(int mid = 1; mid < tot; mid <<= 1)
{
ll w1 = mod_pow(g, (mod - 1) / (2 * mid));
if(inv == -1) w1 = mod_pow(w1, mod - 2);
for(int i = 0; i < tot; i += 2 * mid)
{
ll wk = 1;
for(int j = 0; j < mid; j++, wk = wk * w1 % mod)
{
ll x = a[i + j], y = wk * a[i + j + mid] % mod;
a[i + j] =(x + y) % mod, a[i + j + mid] = (x - y + mod) % mod;
}
}
}
}
int main()
{
int n, t;
scanf("%d", &n);
scanf("%s", str + 1);
scanf("%d", &t);
for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
ll tmp = 0;
for(int i = 1; str[i]; i++)
{
tmp = (tmp * 10 + str[i] - '0') % mod;
}
ll k = tmp;
f[1] = 1;
if(t == 0) for(int i = 2; i <= n; i++)
{
f[i] = f[i - 1] * (k + i - 2) % mod * mod_pow(i - 1, mod - 2) % mod;
}
else
{
for(int i = 2; i <= n; i++)
f[i] = f[i - 1] * (k - i + 2) % mod * mod_pow(i - 1, mod - 2) % mod;
for(int i = 2; i <= n; i += 2)
f[i] = mod - f[i];
}
while((1 << bit) <= n + n) bit++;
tot = 1 << bit;
for(int i = 0; i < tot; i++)
{
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
}
ntt(a, 1), ntt(f, 1);
for(int i = 0; i < tot; i++) a[i] = a[i] * f[i] % mod;
ntt(a, -1);
ll inv = mod_pow(tot, mod - 2);
for(int i = 1; i <= n; i++)
{
printf("%lld ", a[i + 1] * inv % mod);
}
return 0;
}
给定序列 g 1 … n − 1 g_{1\dots n - 1} g1…n−1,求序列 f 0 … n − 1 f_{0\dots n - 1} f0…n−1。
其中 f i = ∑ j = 1 i f i − j g j f_i=\sum_{j=1}^if_{i-j}g_j fi=∑j=1ifi−jgj,边界为 f 0 = 1 f_0=1 f0=1。
答案对 998244353 998244353 998244353 取模。
不妨设 F ( x ) = ∑ i = 0 ∞ f i x i , G ( x ) = ∑ i = 0 ∞ g i x i F(x)=\sum_{i=0}^{\infty}f_ix^i,G(x)=\sum_{i=0}^{\infty}g_ix^i F(x)=∑i=0∞fixi,G(x)=∑i=0∞gixi,且 g 0 = 0 g_0=0 g0=0
那么有 F ( x ) G ( x ) = ∑ i = 0 ∞ x i ∑ j + k = i f j g k = F ( x ) − f 0 x 0 F(x)G(x)=\sum_{i=0}^{\infty}x^i\sum_{j+k=i}f_jg_k=F(x)-f_0x^0 F(x)G(x)=∑i=0∞xi∑j+k=ifjgk=F(x)−f0x0
即 F ( x ) G ( x ) ≡ F ( x ) − f 0 ( m o d x n ) F(x)G(x) \equiv F(x)-f_0 (\bmod x^n) F(x)G(x)≡F(x)−f0(modxn)
即 F ( x ) ≡ f 0 1 − G ( x ) ( m o d x n ) F(x) \equiv \dfrac{f_0}{1-G(x)} (\bmod x^n) F(x)≡1−G(x)f0(modxn)
即 F ( X ) ≡ ( 1 − G ( x ) ) − 1 ( m o d x n ) F(X) \equiv (1-G(x))^{-1} (\bmod x^n) F(X)≡(1−G(x))−1(modxn)
#include
using namespace std;
typedef long long ll;
const ll mod = 998244353, g = 3;
const int N = 300010;
ll a[N], b[N], c[N];
int rev[N];
ll mod_pow(ll x, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
void ntt(ll a[], int tot, int type)
{
for(int i = 0; i < tot; i++)
{
if(i < rev[i]) swap(a[i], a[rev[i]]);
}
for(int mid = 1; mid < tot; mid <<= 1)
{
ll w1 = mod_pow(g, (mod - 1) / (2 * mid));
if(type == -1) w1 = mod_pow(w1, mod - 2);
for(int i = 0; i < tot; i += 2 * mid)
{
ll wk = 1;
for(int j = 0; j < mid; j++, wk = wk * w1 % mod)
{
ll x = a[i + j], y = wk * a[i + j + mid] % mod;
a[i + j] = (x + y) % mod, a[i + j + mid] = (x - y + mod) % mod;
}
}
}
if(type == -1)
{
ll inv = mod_pow(tot, mod - 2);
for(int i = 0; i < tot; i++) a[i] = a[i] * inv % mod;
}
}
void polyinv(int n)
{
b[0] = mod_pow(a[0], mod - 2);
for(int t = 2; t <= 2 * n; t <<= 1)
{
int t2 = t << 1;
int bit = 0;
while((1 << bit) < t2) bit++;
for(int i = 0; i < t2; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
copy(a, a + t, c);
ntt(b, t2, 1), ntt(c, t2, 1);
for(int i = 0; i < t2; i++)
{
b[i] = b[i] * (2 - b[i] * c[i] % mod + mod) % mod;
}
ntt(b, t2, -1);
fill(b + t, b + t2, 0);
}
}
int main()
{
int n;
scanf("%d", &n);
a[0] = 1;
for(int i = 1; i < n; i++)
{
scanf("%lld", &a[i]);
a[i] = a[i] ? mod - a[i] : 0;
}
polyinv(n);
for(int i = 0; i < n; i++) printf("%lld ", b[i]);
return 0;
}
给定一个大小为 n ( 1 ≤ n ≤ 5 × 1 0 5 ) n(1\leq n \leq 5 \times10^5) n(1≤n≤5×105) 的集合 S = { a 0 , a 1 , . . . , a n − 1 } S=\{a_0,a_1,...,a_{n-1}\} S={a0,a1,...,an−1}. 求最小的正整数 s e e d seed seed,使得对于任意 i i i ( 0 ≤ i ≤ n ) (0\leq i \leq n) (0≤i≤n), a i ( m o d s e e d ) a_i \pmod {seed} ai(modseed) 的结果各不相同。
若存在 a i ≡ a j ( m o d s e e d ) a_i \equiv a_j \pmod {seed} ai≡aj(modseed),则 a i − a j ≡ 0 ( m o d s e e d ) a_i - a_j \equiv 0\pmod {seed} ai−aj≡0(modseed)。这说明若 s e e d seed seed 不是合法解,则存在 a i − a j a_i - a_j ai−aj 是 s e e d seed seed 的倍数
换句话说,如果我们知道了所有 a i − a j a_i - a_j ai−aj 的值,就可以用枚举因数的方法找到最小的 s e e d seed seed.
如果求 a i − a j a_i - a_j ai−aj 的值呢?并不好求,但是我们发现可以用卷积的方法快速求出 a i + a j a_i + a_j ai+aj 的值。因此我们可以令 b i = 500000 − a i b_i = 500000 - a_i bi=500000−ai. 然后两个多项式卷积即可.
注意最小的 s e e d seed seed 可能是 500001 500001 500001.
#include
using namespace std;
const int N = 2000010;
typedef long long ll;
const ll mod = 998244353, g = 3;
ll mod_pow(ll x, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
ll a[N], b[N];
int rev[N], tot, bit;
void ntt(ll a[], int tot, int type)
{
for(int i = 0; i < tot; i++)
{
if(i < rev[i]) swap(a[i], a[rev[i]]);
}
for(int mid = 1; mid < tot; mid <<= 1)
{
ll w1 = mod_pow(g, (mod - 1) / (2 * mid));
if(type == -1) w1 = mod_pow(w1, mod - 2);
for(int i = 0; i < tot; i += 2 * mid)
{
ll wk = 1;
for(int j = 0; j < mid; j++, wk = wk * w1 % mod)
{
ll x = a[i + j], y = wk * a[i + j + mid] % mod;
a[i + j] = (x + y) % mod, a[i + j + mid] = (x - y + mod) % mod;
}
}
}
if(type == -1)
{
ll inv = mod_pow(tot, mod - 2);
for(int i = 0; i < tot; i++)
{
a[i] = a[i] * inv % mod;
}
}
}
int main()
{
int n;
scanf("%d", &n);
int m = 500000;
for(int i = 1; i <= n; i++)
{
int x;
scanf("%d", &x);
a[x] = b[m - x] = 1;
}
while((1 << bit) <= 2 * m + 1) bit++;
tot = 1 << bit;
for(int i = 0; i < tot; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
ntt(a, tot, 1), ntt(b, tot, 1);
for(int i = 0; i < tot; i++) a[i] = a[i] * b[i] % mod;
ntt(a, tot, -1);
int ans = -1;
for(int i = 1; i <= m; i++)
{
bool flag = false;
for(int j = i; j <= m; j += i)
{
if(a[j + m])
{
flag = true;
break;
}
}
if(!flag)
{
abs = i;
break;
}
}
if(ans == -1) ans = m + 1;
printf("%d\n", ans);
return 0;
}
题意
给定 ( n , k , D ) (n,k,D) (n,k,D),求
∑ a i ≥ 0 , ∑ i = 1 n a i = D D ! ∏ i = 1 n ( a i + k ) ! \sum_{a_i\ge0,\sum_{i=1}^n a_i=D}\dfrac{D!}{\prod_{i=1}^n(a_i+k)!} ∑ai≥0,∑i=1nai=D∏i=1n(ai+k)!D!
1 ≤ n ≤ 50 , 0 ≤ k ≤ 50 , 0 ≤ D ≤ 1 0 8 1\le n\le 50,0\le k\le 50,0\le D\le 10^8 1≤n≤50,0≤k≤50,0≤D≤108
由于 e x = ∑ k = 0 ∞ x k k ! e^x=\sum_{k=0}^{\infty}\dfrac{x^k}{k!} ex=∑k=0∞k!xk,所以有
∑ a i ≥ 0 , ∑ i = 1 n a i = D 1 ∏ i = 1 n a i ! = [ x D ] ( e x ) n = [ x D ] e n x \sum_{a_i\ge0,\sum_{i=1}^n a_i=D}\dfrac{1}{\prod_{i=1}^na_i!}=[x^D](e^x)^n=[x^D]e^{nx} ∑ai≥0,∑i=1nai=D∏i=1nai!1=[xD](ex)n=[xD]enx
设 b i = a i + k b_i=a_i+k bi=ai+k,则原问题化为求
∑ b i ≥ k , ∑ i = 1 n b i = D + n k D ! ∏ i = 1 n b i ! \sum_{b_i\ge k,\sum_{i=1}^n b_i=D+nk}\dfrac{D!}{\prod_{i=1}^nb_i!} ∑bi≥k,∑i=1nbi=D+nk∏i=1nbi!D!
考虑 b i ≥ k b_i\ge k bi≥k 的限制,只需将次数 < k
∑ b i ≥ k , ∑ i = 1 n b i = D + n k 1 ∏ i = 1 n b i ! = [ x D + n k ] ( e x − ∑ i = 0 k − 1 x i i ! ) n \sum_{b_i\ge k,\sum_{i=1}^n b_i=D+nk}\dfrac{1}{\prod_{i=1}^nb_i!}=[x^{D+nk}](e^x-\sum_{i=0}^{k-1}\dfrac{x^i}{i!})^n ∑bi≥k,∑i=1nbi=D+nk∏i=1nbi!1=[xD+nk](ex−∑i=0k−1i!xi)n
由于 ( n , k ) (n,k) (n,k) 很小,上式可以直接暴力计算,记 A ( x ) = − ∑ i = 0 k − 1 x i i ! A(x)=-\sum_{i=0}^{k-1}\dfrac{x^i}{i!} A(x)=−∑i=0k−1i!xi,预处理 A ( x ) , A 2 ( x ) , . . . , A n ( x ) A(x),A^2(x),...,A^n(x) A(x),A2(x),...,An(x)
再根据 ( e x + A ( x ) ) n = ∑ i = 0 n ( n i ) A i ( x ) e ( n − i ) x (e^x+A(x))^n=\sum_{i=0}^{n}\binom{n}{i}A^i(x)e^{(n-i)x} (ex+A(x))n=∑i=0n(in)Ai(x)e(n−i)x 即可算出答案。
计算过程中出现的 1 ( D + n k − j ) ! , j ∈ [ 0 , A . l e n ] \dfrac{1}{(D+nk-j)!},j\in[0,A.len] (D+nk−j)!1,j∈[0,A.len] 无法快速计算,但由于整体乘上了 D ! D! D!,而 D ! ( D + n k − j ) ! \dfrac{D!}{(D+nk-j)!} (D+nk−j)!D! 可以快速计算。
最终复杂度 O ( n 2 k 2 ) O(n^2k^2) O(n2k2)
#include
using namespace std;
const int N = 2510;
typedef long long ll;
const ll mod = 998244353;
ll n, k, D, tot, a[N][N], f[N];
ll fact[N], infact[N];
ll mod_pow(ll x, ll n)
{
ll res = 1;
while(n)
{
if(n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
void mul(ll a[], ll b[], ll c[])
{
for(int i = 0; i <= tot; i++)
{
for(int j = 0; j <= tot; j++)
{
c[i] += a[j] * b[i - j] % mod;
c[i] %= mod;
}
}
}
ll C(ll a, ll b)
{
return fact[a] * infact[b] % mod * infact[a - b] % mod;
}
int main()
{
scanf("%lld%lld%lld", &n, &k, &D);
tot = n * k;
fact[0] = infact[0] = 1;
for(int i = 1; i <= 50; i++)
{
fact[i] = fact[i - 1] * i % mod;
}
infact[50] = mod_pow(fact[50], mod - 2);
for(int i = 50 - 1; i >= 0; i--) infact[i] = infact[i + 1] * (i + 1) % mod;
f[0] = 1;
for(ll i = 1; i <= tot; i++)
{
ll u = i + D;
f[i] = f[i - 1] * mod_pow(u, mod - 2) % mod;
}
a[0][0] = 1;
for(int i = 0; i < k; i++) a[1][i] = mod - infact[i];
for(int i = 2; i <= n; i++) mul(a[1], a[i - 1], a[i]);
ll ans = 0;
for(int i = 0; i <= n; i++)
{
ll res = 0;
for(int j = 0; j <= tot; j++)
{
res += mod_pow(i, D + tot - j) * f[tot -j] % mod * a[n - i][j] % mod;
res %= mod;
}
ans += res % mod * C(n, i) % mod;
ans %= mod;
}
printf("%lld\n", ans);
return 0;
}
一个东西会按某个固定的概率随机生成 [ 1 , n ] [1,n] [1,n] 内的数,生成数字 i i i 的概率是 p i p_i pi。每生成一个数之后,若它是包含之前所生成的所有数中最大的(可能是并列最大),那就再继续生成;否则停止生成,然后得分是目前为止生成序列的长度的平方。求得分的期望.
如果生成每个数字的数量都确定了,那么可以唯一确定尚未停止的合法的生成序列的前缀(比如选择的数字排好序之后就是 1 , 1 , 2 , 3 , 4 , 5 1, 1, 2, 3, 4, 5 1,1,2,3,4,5). 那么数字 i i i 对应的多项式就是
1 + p i x + ( p i x ) 2 + ( p i x ) 3 + . . . = 1 1 − p i x 1 + p_ix + (p_ix)^2 + (p_ix)^3 + ... = \dfrac{1}{1 - p_ix} 1+pix+(pix)2+(pix)3+...=1−pix1
设 f ( x ) = ∏ i = 1 n 1 1 − p i x f(x) = \prod\limits_{i=1}^n \dfrac{1}{1-p_ix} f(x)=i=1∏n1−pix1
则 x i x^i xi 对应的系数记为序列长度大于 i i i 的概率,即 P ( l e n > i ) P(len > i) P(len>i). 即 f ( x ) = ∑ i = 0 ∞ P ( l e n > i ) x i f(x) = \sum\limits_{i=0}^\infty P(len > i)x^i f(x)=i=0∑∞P(len>i)xi
那么期望为
∑ i = 0 ∞ ( i + 1 ) 2 ( P ( l e n > i ) − P ( l e n > i + 1 ) ) = ∑ i = 0 ∞ i 2 P ( l e n > i ) + ∑ i = 0 ∞ ( 2 i + 1 ) P ( l e n > i ) − ∑ i = 0 ∞ ( i + 1 ) 2 P ( l e n > i + 1 ) = ∑ i = 0 ∞ ( 2 i + 1 ) P ( l e n > i ) = 2 ∑ i = 0 ∞ i ∗ P ( l e n > i ) + ∑ i = 0 ∞ P ( l e n > i ) = 2 f ′ ( 1 ) + f ( 1 ) . \begin{align} &\sum\limits_{i=0}^\infty (i+1)^2\Big(P(len > i) - P(len > i + 1)\Big) \\ =& \sum\limits_{i=0}^\infty i^2P(len > i) + \sum\limits_{i=0}^\infty (2i+1) P(len > i) - \sum\limits_{i=0}^\infty (i+1)^2 P(len > i + 1) \\ =& \sum\limits_{i=0}^\infty (2i+1) P(len > i) \\ =&2 \sum\limits_{i=0}^\infty i*P(len>i) + \sum\limits_{i=0}^\infty P(len > i) \\ =& 2f'(1) + f(1). \end{align} ====i=0∑∞(i+1)2(P(len>i)−P(len>i+1))i=0∑∞i2P(len>i)+i=0∑∞(2i+1)P(len>i)−i=0∑∞(i+1)2P(len>i+1)i=0∑∞(2i+1)P(len>i)2i=0∑∞i∗P(len>i)+i=0∑∞P(len>i)2f′(1)+f(1).
且 f ′ ( x ) = f ( x ) ∑ i = 1 n p i 1 − p i x f'(x) = f(x) \sum\limits_{i=1}^n \dfrac{p_i}{1 - p_ix} f′(x)=f(x)i=1∑n1−pixpi
代码略.
求出 n n n 个点的简单 (无重边无自环) 有标号无向连通图数目
设 n n n 个点的简单有标号无向连通图的数量是 f ( n ) f(n) f(n), n n n 个点的简单有标号无向图数量是 g ( n ) g(n) g(n). 则 g ( n ) = 2 C n 2 g(n) = 2^{C_n^2} g(n)=2Cn2
我们枚举 1 1 1 号点所在连通块儿的大小,可得:
g ( n ) = ∑ i = 1 n C n − 1 i − 1 f ( i ) ∗ g ( n − i ) g(n) = \sum\limits_{i=1}^nC_{n-1}^{i-1} f(i) * g(n-i) g(n)=i=1∑nCn−1i−1f(i)∗g(n−i)
化简可得:
g ( n ) ( n − 1 ) ! = ∑ i = 1 n f ( i ) ( i − 1 ) ! g ( n − i ) ( n − i ) ! \frac{g(n)}{(n-1)!} = \sum\limits_{i=1}^n \frac{f(i)}{(i-1)!} \frac{g(n-i)}{(n-i)!} (n−1)!g(n)=i=1∑n(i−1)!f(i)(n−i)!g(n−i)
设
F ( x ) = ∑ i = 1 ∞ f ( i ) ( i − 1 ) ! x i G ( x ) = ∑ i = 0 ∞ g ( i ) i ! x i P ( x ) = ∑ i = 1 ∞ g ( i ) ( i − 1 ) ! x i F ( x ) = P ( x ) ∗ G − 1 ( x ) \begin{align} F(x) &= \sum\limits_{i=1}^\infty \frac{f(i)}{(i-1)!}x^i \\ G(x) &= \sum\limits_{i=0}^\infty \frac{g(i)}{i!}x^i \\ P(x) &= \sum\limits_{i=1}^\infty \frac{g(i)}{(i-1)!}x^i\\ F(x) &= P(x) * G^{-1}(x) \end{align} F(x)G(x)P(x)F(x)=i=1∑∞(i−1)!f(i)xi=i=0∑∞i!g(i)xi=i=1∑∞(i−1)!g(i)xi=P(x)∗G−1(x)
这个背包最多可以装 1 0 5 10^5 105 大小的东西
付公主有 n n n 种商品,她要准备出摊了
每种商品体积为 v i v_i vi,都有无限件
给定 m m m,对于 s ∈ [ 1 , m ] s\in [1,m] s∈[1,m],请你回答用这些商品恰好装 s s s 体积的方案数
设方案数序列为 s s s,则其生成函数
s ( x ) = ∏ i = 1 n 1 1 − x v i s(x) = \prod\limits_{i=1}^n\dfrac{1}{1 - x^{v_i}} s(x)=i=1∏n1−xvi1
两边取对数,对每个 ln ( 1 − x v i ) \ln(1-x^{v_i}) ln(1−xvi) 在 x = 0 x=0 x=0 泰勒展开
ln s ( x ) = − ∑ i = 1 n ln ( 1 − x v i ) = ∑ i = 1 n ∑ j = 1 ∞ 1 j ( x v i ) j = ∑ i = 1 n ∑ j = 1 ∞ 1 j x j ∗ v i \begin{align} \ln s(x) &= -\sum\limits_{i=1}^n \ln(1-x^{v_i}) \\ &= \sum\limits_{i=1}^n \sum\limits_{j=1}^\infty \frac{1}{j} (x^{v_i})^j \\ &= \sum\limits_{i=1}^n \sum\limits_{j=1}^\infty \frac{1}{j} x^{j*v_i} \\ \end{align} lns(x)=−i=1∑nln(1−xvi)=i=1∑nj=1∑∞j1(xvi)j=i=1∑nj=1∑∞j1xj∗vi
然后两边在同时求指数函数就可以了. 答案就是
exp { ∑ i = 1 n ∑ j = 1 ∞ 1 j x j ∗ v i } \exp\bigl\{\sum\limits_{i=1}^n \sum\limits_{j=1}^\infty \dfrac{1}{j} x^{j*v_i} \bigr\} exp{i=1∑nj=1∑∞j1xj∗vi}
生成函数
FWT变形
FFT+字符串