题目大意
小 C
现在有 \(n\) 个分身,每个分身有 \(a_i\) 个奖杯。
现在会按照从 \(1\) 到 \(n\) 的顺序,每个分身会将自己有的所有奖杯一个一个地随机地分给其它 \(n - 1\) 个分身。
问最后每个分身期望有的奖杯的个数,对 \(998244353\) 取模。
对于 \(100\%\) 的数据,\(2 \le n \le 10^6\), \(0 \le a_i < 998244353\)。
解题思路
简单的数学期望。
不会的点 这里。
显然有暴力,\(\mathcal O(n^2)\)。
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j)
{
if(i == j) continue;
f[j] = f[j] + f[i] * x % mod;
f[j] = f[j] % mod;
}
f[i] = 0;
}
线段树优化,\(\mathcal O(n \log n)\),可过。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if(c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
int n;
int a[1000007];
int f[1000007];
const int mod = 998244353;
int tr[1000007 << 2], lazy[1000007 << 2];
void push_down(int o, int l, int r)
{
if(lazy[o])
{
int mid = (l + r) >> 1;
tr[o << 1] = (tr[o << 1] + (mid - l + 1) * lazy[o] % mod) % mod;
tr[o << 1 | 1] = (tr[o << 1 | 1] + (r - mid) * lazy[o] % mod) % mod;
lazy[o << 1] = (lazy[o << 1] + lazy[o]) % mod;
lazy[o << 1 | 1] = (lazy[o << 1 | 1] + lazy[o]) % mod;
lazy[o] = 0;
}
}
void build(int o, int l, int r)
{
if(l == r)
{
tr[o] = a[l];
return;
}
int mid = (l + r) >> 1;
build(o << 1, l, mid);
build(o << 1 | 1, mid + 1, r);
tr[o] = (tr[o << 1] + tr[o << 1 | 1]) % mod;
}
void update(int o, int l, int r, int L, int R, int k)
{
if(L <= l && r <= R)
{
tr[o] = (tr[o] + (r - l + 1) * k % mod) % mod;
lazy[o] = (lazy[o] + k) % mod;
return;
}
int mid = (l + r) >> 1;
push_down(o, l, r);
if(L <= mid) update(o << 1, l, mid, L, R, k);
if(R > mid) update(o << 1 | 1, mid + 1, r, L, R, k);
tr[o] = (tr[o << 1] + tr[o << 1 | 1]) % mod;
}
int query(int o, int l, int r, int L, int R)
{
if(L <= l && r <= R)
{
return tr[o];
}
int mid = (l + r) >> 1;
push_down(o, l, r);
int res = 0;
if(L <= mid) res = (res + query(o << 1, l, mid, L, R) % mod) % mod;
if(R > mid) res = (res + query(o << 1 | 1, mid + 1, r, L, R) % mod) % mod;
tr[o] = (tr[o << 1] + tr[o << 1 | 1]) % mod;
return res % mod;
}
int qpow(int x, int y)
{
int ans = 1ull;
while(y)
{
if(y % 2 == 1ull) ans = ans * x % mod;
x = x * x % mod;
y /= 2;
}
return ans % mod;
}
signed main()
{
n = read();
int x = qpow(n - 1, mod - 2) % mod;
for(int i = 1; i <= n; ++i)
{
a[i] = read();
}
build(1, 1, n);
for(int i = 1; i <= n; ++i)
{
int p = query(1, 1, n, i, i);
if(i == 1)
{
update(1, 1, n, i + 1, n, p % mod * x % mod);
}
else if(i == n)
{
update(1, 1, n, 1, i - 1, p % mod * x % mod);
}
else
{
update(1, 1, n, 1, i - 1, p % mod * x % mod);
update(1, 1, n, i + 1, n, p % mod * x % mod);
}
update(1, 1, n, i, i, -p);
}
for(int i = 1; i <= n; ++i)
{
int p = query(1, 1, n, i, i);
printf("%lld%s", p, i == n ? "\n" : " ");
}
return 0;
}
根据期望线性,可以设 \(f_i\) 表示操作到第 \(i\) 个分身时第 \(i\) 个分身奖杯的期望个数。
那么 \(ans_i\) 就是所有 $j > i 的 $ \(\frac{f_j}{n - 1}\) 的和,\(f_i\) 的转移也是类似的。
时间复杂度 \(\mathcal O(n)\)。
CODE
#include <bits/stdc++.h>
#define int long long
using namespace std;
int read()
{
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if(c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
int n;
const int mod = 998244353;
int a[1000007];
int sum;
int ans[1000007];
int p;
int qpow(int x, int y)
{
int ans = 1ll;
while(y)
{
if(y & 1) ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans;
}
signed main()
{
n = read();
p = qpow(n - 1, mod - 2);
for(int i = 1; i <= n; ++i)
a[i] = read();
for(int i = 1; i <= n; ++i)
{
int v = ((a[i] + sum) * p) % mod;
sum = (sum + v) % mod;
ans[i - 1] = v;
}
for(int i = n; i >= 1; --i)
ans[i] = (ans[i] + ans[i + 1]) % mod;
for(int i = 1; i <= n; ++i)
printf("%lld ", ans[i]);
return 0;
}