题意:输入p,s,r。
a 1 + a 2 + . . . + a p = s a 1 > = r , a i > = 0 a_1+a_2+...+a_p=s\\ a_1>=r,a_i>=0\\ a1+a2+...+ap=sa1>=r,ai>=0
a 1 a_1 a1如果是最大值,最大值有k个,则有 1 k \frac{1}{k} k1几率获胜,求获胜的概率。
解法:
x 1 + x 2 + . . . + x m = n x i > = 0 x_1+x_2+...+x_m=n\\ x_i>=0 x1+x2+...+xm=nxi>=0
有 ( n + m − 1 m − 1 ) \binom{n+m-1}{m-1} (m−1n+m−1)个解,隔板法。
x 1 + x 2 + . . . + x m = n x i > = k x_1+x_2+...+x_m=n\\ x_i>=k x1+x2+...+xm=nxi>=k
有 ( n − m ∗ k − 1 m − 1 ) \binom{n-m*k-1}{m-1} (m−1n−m∗k−1)个解,这个只需要把上面式子换成 y 1 + y 1 + . . . + y m = n − m ∗ k . y i > = 0 y_1+y_1+...+y_m=n-m*k.y_i>=0 y1+y1+...+ym=n−m∗k.yi>=0。
x 1 + x 2 + . . . + x m = n x i < = k x_1+x_2+...+x_m=n\\ x_i<=k x1+x2+...+xm=nxi<=k
有 ∑ i = 0 m ( − 1 ) i ( m i ) ( n − i ∗ ( k + 1 ) + m − 1 m − 1 ) \sum_{i=0}^{m}(-1)^i\binom{m}{i}\binom{n-i*(k+1)+m-1}{m-1} ∑i=0m(−1)i(im)(m−1n−i∗(k+1)+m−1)个解,对上式进行容斥。
对这道题只考虑 a 1 a_1 a1是最大值的时候,枚举 a 1 a_1 a1的值,枚举与 a 1 a_1 a1值相同的个数,其他值小于 a 1 a_1 a1。复杂度 O ( p ∗ p ∗ s ) O(p*p*s) O(p∗p∗s)
#include
using namespace std;
const int mod = 998244353;
const int N = 6000;
int fac[N], fav[N];
int a[N];
int qpow(int x, int y)
{
int ans = 1;
while (y)
{
if (y & 1)
ans = 1ll * ans * x % mod;
x = 1ll * x * x % mod;
y /= 2;
}
return ans;
}
void init()
{
fac[0] = 1;
for (int i = 1; i < N; i++)
fac[i] = 1ll * fac[i - 1] * i % mod;
fav[N - 1] = qpow(fac[N - 1], mod - 2);
for (int i = N - 2; i >= 0; i--)
fav[i] = 1ll * fav[i + 1] * (i + 1) % mod;
}
int C(int n, int m)
{
if (n == m)
return 1;
if (n < 0 || m < 0 || n < m)
return 0;
return 1ll * fac[n] * fav[m] % mod * fav[n - m] % mod;
}
int main()
{
init();
int p, s, r;
scanf("%d%d%d", &p, &s, &r);
int ans = 0;
for (int i = r; i <= s; i++)
{
for (int j = 1; j <= p; j++)
{
if (j * i > s)
break;
int tmp = 0;
int inv = 1ll * qpow(j, mod - 2) * C(p - 1, j - 1) % mod;
for (int k = 0, d = 1; k <= p - j; k++, d *= -1)
{
tmp = (tmp + 1ll * d * C(p - j, k) * C(s - i * j + (p - j) - 1 - k * (i), p - j - 1) % mod) % mod;
tmp = (tmp + mod) % mod;
}
ans = (ans + 1ll * tmp * inv % mod) % mod;
}
}
ans = 1ll * ans * qpow(C(s - r + p - 1, p - 1), mod - 2) % mod;
printf("%d\n", ans);
return 0;
}
题意:n的排列,其中有些值不确定,用-1表示,这个排列可能是满足条件的任意一个排列(等概率)。求逆序数的期望。
解法:确定数之间:如果序列中没有-1,就是求序列的逆序数。不确定数与确定数:对一个确定的值 a i a_i ai来说,会对答案有贡献当前面有大于 a i a_i ai的数出现,或者后面有小于 a i a_i ai的数出现,对于小于(大于) a i a_i ai的值一起计算。不确定数之间:假设一共m个-1,相当于点对 i , j i,j i,j等概率的一前一后,期望是 1 2 \frac{1}{2} 21,所以对答案的贡献是 ( n − 1 2 ) ∗ 1 2 \binom{n-1}{2}*\frac{1}{2} (2n−1)∗21,复杂度 O ( n l o g n + n + 1 ) O(nlogn+n+1) O(nlogn+n+1)
#include
using namespace std;
const int N = 2e5 + 5;
const int mod = 998244353;
int a[N], fac[N], fav[N], sum[N];
int dp1[N], dp2[N];
int lowbit(int x)
{
return x & (-x);
}
void update(int x)
{
while (x < N)
{
sum[x]++;
x += lowbit(x);
}
}
int query(int x)
{
int ans = 0;
while (x)
{
ans += sum[x];
x -= lowbit(x);
}
return ans;
}
int qpow(int x, int y)
{
int ans = 1;
while (y)
{
if (y & 1)
ans = 1ll * ans * x % mod;
x = 1ll * x * x % mod;
y /= 2;
}
return ans;
}
void init()
{
fac[0] = 1;
for (int i = 1; i < N; i++)
fac[i] = 1ll * fac[i - 1] * i % mod;
fav[N - 1] = qpow(fac[N - 1], mod - 2);
for (int i = N - 2; i >= 0; i--)
{
fav[i] = 1ll * fav[i + 1] * (i + 1) % mod;
}
}
int main()
{
init();
int n, num = 0;
scanf("%d", &n);
for(int i=1;i<=n;i++) dp2[i]=1;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
if (a[i] == -1)
{
dp1[i] = 1;
num++;
}
else{
dp2[a[i]]=0;
}
}
for (int i = 1; i <= n; i++)
{
dp1[i] += dp1[i - 1];
dp2[i] += dp2[i - 1];
}
int ans = 0;
for (int i = n; i >= 1; i--)
{
if (a[i] != -1)
{
ans = (ans + query(a[i])) % mod;
update(a[i]);
}
}
if (num == 0)
{
printf("%d\n", ans);
return 0;
}
int inv = 1ll * fac[num - 1] * fav[num] % mod;
for (int i = 1; i <= n; i++)
{
if (a[i] != -1)
{
ans = (ans + 1ll * inv * dp1[i] % mod * (num - dp2[a[i]]) % mod) % mod;
ans = (ans + 1ll * inv * (num - dp1[i]) % mod * dp2[a[i]] % mod) % mod;
}
}
ans = (ans + 1ll * num * (num - 1) % mod * qpow(4, mod - 2) % mod) % mod;
printf("%d\n", ans);
return 0;
}
题意:长度为n(偶数)的数,每一位只能是序列 a i a_i ai中的值,求前一半的和等于后一半的和的序列的个数。
解法:
求用 a i a_i ai形成的和为m的序列个数,可以用简单的dp,但是复杂度过高。
通过生成函数求的话,相当于求多项式
( x a 1 + x a 2 + . . . + a a m ) n (x^{a_1}+x^{a_2}+...+a^{a_m})^n (xa1+xa2+...+aam)n
用NTT进行多项式乘法,用快速幂减少乘法次数。
#include
#define LL long long int
#define ls (x << 1)
#define rs (x << 1 | 1)
#define MID int mid=(l+r)>>1
using namespace std;
const int N = 2100000;
const int Mod = 998244353;
int n, m, L, R[N], a[N], d[20], ans[N];
inline int QPow(int d, int z)
{
int ans = 1;
for (; z; z >>= 1, d = 1ll * d * d % Mod)
if (z & 1)
ans = 1ll * ans * d % Mod;
return ans;
}
inline void NTT(int *A, int f)
{
for (int i = 0; i < n; ++i)
if (i < R[i])
swap(A[i], A[R[i]]);
for (int i = 1; i < n; i <<= 1)
{
int gn = QPow(3, (Mod - 1) / (i << 1)), x, y;
for (int j = 0; j < n; j += (i << 1))
{
int g = 1;
for (int k = 0; k < i; ++k, g = 1ll * g * gn % Mod)
{
x = A[j + k];
y = 1ll * g * A[i + j + k] % Mod;
A[j + k] = (x + y) % Mod;
A[i + j + k] = (x - y + Mod) % Mod;
}
}
}
if (f == 1)
return;
reverse(A + 1, A + n);
int y = QPow(n, Mod - 2);
for (int i = 0; i < n; ++i)
A[i] = 1ll * A[i] * y % Mod;
}
void qpow(int *x, int y)
{
for (int i = 0; i < n; i++)
ans[i] = 1;
while (y)
{
if (y & 1)
{
for (int i = 0; i < n; i++)
ans[i] = 1ll * ans[i] * x[i] % Mod;
}
for (int i = 0; i < n; i++)
{
x[i] = 1ll * x[i] * x[i] % Mod;
}
y /= 2;
}
}
int main()
{
n = 1000000;
m = 1000000;
m += n;
for (n = 1; n <= m; n <<= 1)
++L;
//printf("%d\n",n);
for (int i = 0; i < n; ++i)
R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
int nn, kk;
scanf("%d%d", &nn, &kk);
for (int i = 1; i <= kk; i++)
{
scanf("%d", &d[i]);
a[d[i]] = 1;
}
NTT(a, 1);
qpow(a, nn / 2);
NTT(ans, -1);
int ANS = 0;
for (int i = 0; i < n; i++)
{
ANS = (ANS + 1ll * ans[i] * ans[i] % Mod) % Mod;
}
printf("%d\n", ANS);
return 0;
}