外教变身萌翻小学员,VIPKID “AR 变脸” 打造趣味互动课堂,这是在线少儿英语品牌 VIPKID 全新推出的辅助教学功能——AR 变脸,外教在上课过程中可以随意选取合适的表情贴纸。人脸识别和表情识别等技术的应用帮助“AR 变脸”这一教辅功能更好地服务外教,更好地激励学生积极地参与课堂内容,提升在线课堂的趣味性。

现在,外教 Michale 就变身了一只萌萌的大熊猫,给你出来了一道关于数列的题目,请看题:

给定长度为 nn 的数列 aa,显然这个数列有很多最长上升子序列,我们等概率地取出一个最长上升子序列,求每个数被选中的概率,对 998244353998244353 取模。

提示:

如果你知道如何表示一个分数在模意义下的值可以直接做题了

对于一个分数 \frac{p}{q}qp,我们用模意义下的整数 p\times q^{-1}p×q−1 表示 \frac{p}{q}qp在模意义下的值。

其中 q^{-1}q−1 表示 qq 在模意义下的逆元,满足 q \times q^{-1}q×q−1 在模意义下等于 11。

对于模数 modmod,且 modmod 为质数时,qq 的逆元等于q^{mod-2}qmod−2

输入格式

第一行输入一个整数 nn 表示数列 aa 的长度。

接下来一行输入 nn 个整数 a_iai。

输出格式

输出 nn 个整数,表示每个数字被选中的概率。

数据规模

a_i \le 10^9,n \le 5 \times 10^5ai≤109,n≤5×105

输出时每行末尾的多余空格,不影响答案正确性

样例输入复制

4
1 2 2 4

样例输出复制

1 499122177 499122177 1

 

分析:

[计蒜之道2019 复赛 A]外教 Michale 变身大熊猫 (线段树求LIS+思维)_表情识别

题解出来基本就知道该怎么写了。

#include<bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1

#define mod 998244353
#define N 4000005
using namespace std;
typedef long long LL;
LL max_len[N], max_num[N], f[N], ff[N], a[N], b[N], g[N], gg[N], n;
//设f[i]为以i结尾的最长上升子序列长度,ff[i]为以i结尾的最长上升子序列的个数,g[i]与gg[i]是反过来求最长下降子序列同理
//max_len表示最长上升子序列的长度(最大值) max_num表示最长上升子序列个数(最大值的个数)
void pushup(int rt)
{
if(max_len[ls] > max_len[rs])
max_len[rt] = max_len[ls], max_num[rt] = max_num[ls];
if(max_len[ls] < max_len[rs])
max_len[rt] = max_len[rs], max_num[rt] = max_num[rs];
if(max_len[ls] == max_len[rs])
max_len[rt] = max_len[ls], max_num[rt] = max_num[ls] + max_num[rs];
max_num[rt] %= mod;
}
void build(int l, int r, int rt)
{
if(l == r)
return;
max_len[rt] = max_num[rt] = 0;
int mid = (l + r) >> 1;
build(l, mid, ls);
build(mid + 1, r, rs);
pushup(rt);
}
//更新x位置贡献,
void update(int l, int r, int x, LL nowlen, LL nownum, int rt)
{
if(l == r)
{
if(max_len[rt] == nowlen)
max_num[rt] += nownum;
else
max_num[rt] = nownum;
max_num[rt] %= mod;
max_len[rt] = nowlen;
return;
}
int mid = (l + r) >> 1;
if(x <= mid)
update(l, mid, x, nowlen, nownum, ls);
else
update(mid + 1, r, x, nowlen, nownum, rs);
pushup(rt);
}
int query1(int l, int r, int L, int R, int rt) //求最大长度值
{
if(L <= l && r <= R)
return max_len[rt];
int mid = (l + r) >> 1, ret = 0;
if(L <= mid)
ret = query1(l, mid, L, R, ls);
if(R > mid)
ret = max(ret, query1(mid + 1, r, L, R, rs));
return ret;
}
//求最大值的个数且保证最大上升子序列长度等于nowlen
int query2(int l, int r, int L, int R, int rt, LL nowlen)
{
if(L <= l && r <= R)
{
if(max_len[rt] == nowlen)
return max_num[rt];//注意要等于最大值才能返回
return 0;
}
int mid = (l + r) >> 1, ret = 0;
if(L <= mid)
ret = query2(l, mid, L, R, ls, nowlen);
if(R > mid)
ret += query2(mid + 1, r, L, R, rs, nowlen), ret %= mod;
return ret;
}
LL qpow(LL y, LL x) //快速幂
{
LL t = y, ans = 1;
for(; x; x >>= 1, t = t * t % mod)
if(x & 1)
ans = ans * t % mod;
return ans;
}
signed main()
{
scanf("%lld", &n);
for(int i = 1; i <= n; i ++)
scanf("%lld", &a[i]), b[i] = a[i];
sort(b + 1, b + 1 + n);
int len=unique(b+1,b+1+n)-(b+1);
for(int i = 1; i <= n; i ++)
a[i] = lower_bound(b + 1, b + 1 + len, a[i]) - b; //离散化


build(0, n, 1);//0是下界,为了1好办

for(int i = 1; i <= n; i ++)
{
f[i] = query1(0, n, 0, a[i] - 1, 1) + 1;//求小于a[i]当中f值最大的数 + 1就是以i结尾的最长上升子序列
ff[i] = query2(0, n, 0, a[i] - 1, 1, f[i] - 1);//同上,最大值的个数就是以i结尾的最长上升子序列的个数
ff[i] = max(ff[i], 1LL);//注意长度和个数至少为1
update(0, n, a[i], f[i], ff[i], 1);//插入
}

int qq = qpow(max_num[1], mod - 2), pp = max_len[1];

memset(max_len, 0, sizeof(max_len));
memset(max_num, 0, sizeof(max_num));
build(1, n + 1, 1);
for(int i = n; i >= 1; i --)
{
g[i] = query1(1, n + 1, a[i] + 1, n + 1, 1) + 1;//同上注意边界
gg[i] = query2(1, n + 1, a[i] + 1, n + 1, 1, g[i] - 1);
gg[i] = max(gg[i], 1LL);
update(1, n + 1, a[i], g[i], gg[i], 1);
}

for(int i = 1; i <= n; i ++)
{
if(f[i] + g[i] - 1 == pp)
printf("%lld ", ff[i] * gg[i] % mod * qq % mod);
else
printf("0 ");
}

return 0;
}