题目
题目链接:https://www.luogu.com.cn/problem/P6834
不幸的是,这棵树尚未长成,只有一个根节点 \(1\)。
Cirno 只能知道这棵树将会有 \(n\) 个结点,上面分别有 \(a_1,a_2,\ldots,a_n\) 颗果实,却无法知道树的形状。
但是树的生长总是具有某种规律。
对于结点 \(i\),它会等概率地从 \([i-k,i-1] \cap N^+\) 中选择一个结点连接,并成为那个节点的子节点。
其中,\(k\) 是一个 Cirno 已经测出的常数。
为了摘下所有的果实,在树长成之后,Cirno 会多次使用魔法。其中每次会在树上选一个联通块,并从联通块内每个结点上摘取一个果子(必须保证该联通块内每个结点都有果子)。
显然,Cirno 会采取最佳策略使得使用魔法的次数最少。
现在,Cirno 已经知道了 \(n\),\(k\) 和每个结点将会长出的果子数 \(a_i\),请你帮她计算出她最少使用的魔法次数的数学期望。为了简单起见,你只需要输出答案除以 \(998244353\) 的余数。
思路
链的情况就是 铺设道路。贪心即可。
放到树上之后,依然考虑贪心。从权值小到大枚举点,那么这个点的贡献应该为点权减去与该点相邻的点的期望深度。
那么分成两部分来计算,第一部分就是编号在 \((x,x+m]\) 中的点,每一个点连向 \(x\) 的期望是不同的,所以我们维护一个树状数组,其中第 \(i\) 位就表示点 \(i\) 的权值除以 \(\min(m,i-1)\)。然后求区间和即可。
对于编号在 \([x-m,x)\) 的点,每一个都有 \(\frac{1}{\min(m,x-1)}\) 的概率连向 \(x\),所以再维护一个树状数组求权值和,然后查询区间权值和除以 \(\min(m,x-1)\) 即可。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000010,MOD=998244353;
int n,m;
ll ans;
struct node
{
int id;
ll x;
}a[N];
bool cmp(node x,node y)
{
return x.x<y.x;
}
ll fpow(ll x,int k)
{
ll ans=1;
for (;k;k>>=1,x=x*x%MOD)
if (k&1) ans=ans*x%MOD;
return ans;
}
struct BIT
{
int c[N];
void add(int x,ll v)
{
for (int i=x;i<=n;i+=i&-i)
c[i]=(c[i]+v)%MOD;
}
ll query(int x)
{
ll ans=0;
for (int i=x;i;i-=i&-i)
ans+=c[i];
return ans%MOD;
}
}bit,bit2;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i].x);
a[i].id=i;
}
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++)
{
ll s=bit.query(min(a[i].id+m,n))-bit.query(a[i].id);
ll s2=(bit2.query(a[i].id-1)-bit2.query(max(a[i].id-m-1,0)))*fpow(min(m,a[i].id-1),MOD-2);
ans=(ans+(a[i].x-s-s2)%MOD+MOD)%MOD;
bit.add(a[i].id,a[i].x*fpow(min(m,a[i].id-1),MOD-2)%MOD);
bit2.add(a[i].id,a[i].x);
}
printf("%lld",ans);
return 0;
}