E Everything Is Generated In Equal Probability
题意;
给出一个
然后在
中选择一个数
,然后在
中选择
个互不相同的数组成一个全排列,然后统计他的逆序数的个数和他子序列的逆序数的个数,求最后逆序数的期望值。
对于一个长度为
的随机排列,总共有
的数对,因为随机的缘故,所以期望的逆序对数就是
。然后其子序列也是同样的做法。对于一个长度为
的子序列,则有
种。所以
可以发现求
的式子里有
, 然后可以把右边和式里的
拎出来放到左边。
AC代码:
const int N = 3000 + 5;
const int MOD = 998244353;
const int mod = 998244353;
ll comb[N][N];
ll f[N];
ll two[N];
ll inv2, inv4;
void init()
{
rep(i, 0, N)
{
comb[i][0] = comb[i][i] = 1;
rep(j, 1, i - 1)
{
comb[i][j] = comb[i - 1][j] + comb[i - 1][j - 1];
comb[i][j] %= MOD;
}
}
}
int main()
{
init();
f[0] = 0;
f[1] = 0;
two[0] = 1;
inv2 = qpow(2, mod - 2, mod);
inv4 = qpow(4, mod - 2, mod);
rep(i, 1, N)
two[i] = two[i - 1] * inv2 % mod;
rep(i, 2, N)
{
f[i] = i * (i - 1) % mod * inv4 % mod;
rep(j, 2, i - 1)
f[i] = (f[i] + comb[i][j] * f[j] % mod * two[i] % mod) % mod;
f[i] = (f[i] * qpow((1 - two[i] + mod) % mod, mod - 2, mod)) % mod;
}
ll n;
while (~sld(n))
{
ll ans = 0;
rep(i, 2, n)
ans = (ans + f[i]) % mod;
ans = ans * qpow(n, mod - 2, mod) % mod;
pld(ans);
}
return 0;
}
J Just Skip The Problem
题意:
两个人玩游戏,给出一个 ,对方心里想一个数,范围是 ,然后你可以提问无数个问题,去确定对方心里想的数,然后输入的数,是代表 ,输出的数,是代表在最少问题的情况下,有多少种提问的方法。
对于每个数使用 个数肯定可以检测出来,这 个数就是 ,因为这样选取的 个数的二进制形式都是 ,然后就可以检测每一位是否为 ,然后就是选取 个数的全排列,就是 。
AC代码;
const int mod = 1e6 + 3;
int main()
{
int n;
while (~sd(n))
{
ll ans = 1;
if (n > mod)
{
puts("0");
continue;
}
rep(i, 1, n)
ans = (ans * i) % mod;
pld(ans);
}
return 0;
}
K - Keen On Everything But Triangle
题意:
给你 个数字, 次查询,每次查询区间 之间可以组成三角形的最大周长是多少。
每次查询区间前三大的,不能组成三角形的话就往下取。
多次建树的主席树模板。
AC代码:
const int maxn = 100010;
const int maxm = maxn * 30;
int q, n, m, tot;
int a[maxn], t[maxn];
int rt[maxn], lson[maxm], rson[maxm], c[maxm];
int build(int l, int r)
{
int root = tot++;
c[root] = 0;
if (l != r)
{
int mid = (l + r) >> 1;
lson[root] = build(l, mid);
rson[root] = build(mid + 1, r);
}
return root;
}
int get_id(ll x)
{
return lower_bound(t + 1, t + 1 + m, x) - t;
}
int update(int root, int pos, int val)
{
int newroot = tot++, tmp = newroot;
c[newroot] = c[root] + val;
int l = 1, r = m;
while (l < r)
{
int mid = (l + r) >> 1;
if (pos <= mid)
{
lson[newroot] = tot++;
rson[newroot] = rson[root];
newroot = lson[newroot];
root = lson[root];
r = mid;
}
else
{
rson[newroot] = tot++;
lson[newroot] = lson[root];
newroot = rson[newroot];
root = rson[root];
l = mid + 1;
}
c[newroot] = c[root] + val;
}
return tmp;
}
int query(int left_root, int right_root, int k)
{
int l = 1, r = m;
while (l < r)
{
int mid = (l + r) >> 1;
if (c[lson[left_root]] - c[lson[right_root]] >= k)
{
r = mid;
left_root = lson[left_root];
right_root = lson[right_root];
}
else
{
l = mid + 1;
k -= c[lson[left_root]] - c[lson[right_root]];
left_root = rson[left_root];
right_root = rson[right_root];
}
}
return l;
}
ll num[55];
int main()
{
while (~sdd(n, q))
{
tot = 0;
rep(i, 1, n)
sd(a[i]);
rep(i, 1, n)
t[i] = a[i];
sort(t + 1, t + 1 + n);
m = unique(t + 1, t + 1 + n) - t - 1; //离散化
rt[n + 1] = build(1, m); //建空树
per(i, 1, n)
{
int pos = get_id(a[i]);
rt[i] = update(rt[i + 1], pos, 1);
}
while (q--)
{
int l, r;
sdd(l, r);
int len = (r - l + 1);
ll ans = -1;
while (len >= 3)
{
int pos1 = query(rt[l], rt[r + 1], len);
int pos2 = query(rt[l], rt[r + 1], len - 1);
int pos3 = query(rt[l], rt[r + 1], len - 2);
if (t[pos1] < t[pos2] + t[pos3])
{
ans = 1LL * t[pos1] + 1LL * t[pos2] + 1LL * t[pos3];
break;
}
len--;
}
pld(ans);
}
}
return 0;
}