【洛谷P6834】梦原

题目

题目链接: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;
}
posted @ 2020-09-19 20:55  stoorz  阅读(143)  评论(0编辑  收藏  举报