题解:P14514 [NFLSPC #8] 如何区分北京东路和北京东路
Solution
此题本人NFLSPC正赛场切。
让我们先一步步分析这个问题。
有
进行
一次爆炸过程(假设选到城市
- 该城市的 危险度 为 当前炸弹威力
a_x 。 - 爆炸后,该城市的炸弹威力 清零(
a_x \to 0 )。 - 其它每个城市
j \neq x 的炸弹威力 增加a_x / (n-1) (保持总威力不变)。
注意:这里“增加
问题转化
设
初始:
一次爆炸选到城市
- 若
i = x :a_i \to 0 。 - 若
i \neq x :a_i \to a_i + \frac{a_x}{n-1} 。
推导递推式
考虑一次随机爆炸(每个城市概率
情况 1:爆炸发生在城市 i (概率 1/n )
- 爆炸后
E_i^{(t+1)} = 0 。
情况 2:爆炸发生在城市 j \neq i (概率 \frac{n-1}{n} 中的某一个 j )
- 对于固定的
j ,概率1/n ,爆炸后:E_i^{(t+1)} = E_i^{(t)} + \frac{E_j^{(t)}}{n-1} 注意这里
E_j^{(t)} 是爆炸前j 的期望威力。
情况 3:爆炸发生在其它城市 p (p \neq i 且 p \neq j 的情况已经包含在情况 2 的求和里)
所以:
对 S^{(t)} 的递推
总威力守恒:每次爆炸只是把
因此
代入化简
合并
计算系数:
所以:
解递推式
这是一个一阶线性递推:
其中
所以稳态时每个城市期望威力相同,等于平均值。
因此:
即:
模运算实现
要求取模
记为:
平均值
则:
时间复杂度
计算总和:
快速幂:
总和:
AC code
#include<bits/stdc++.h>
using namespace std;
const int M=998244353;
long long modpow(long long a,long long b)
{
long long r=1;
while(b)
{
if(b&1) r=r*a%M;
a=a*a%M;
b/=2;
}
return r;
}
int n,k,a[1000003];
int main()
{
cin>>n>>k;
long long s=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s=(s+a[i])%M;
}
long long inv_n=modpow(n,M-2),avg=s*inv_n%M,p=n-2,q=n-1,coef=modpow(p,k)*modpow(modpow(q,k),M-2)%M;
for(int i=1;i<=n;i++)
{
long long res=(avg+coef*(a[i]-avg+M))%M;
cout<<res<<" ";
}
return 0;
}