正题
题目链接:https://www.luogu.com.cn/problem/P6046
题目大意
nnn个数,每次选择两个相邻的数删除小的那个,求每个数期望存活轮数。
解题思路
相当于一条链每次缩掉一条边,我们发现其实每个点只需要考虑左右第一个比它大的就好了。定义P(x)P(x)P(x)表示xxx轮攻击后还没有死亡的概率,有ans=∑i=1n−1P(i)ans=\sum_{i=1}^{n-1}P(i)ans=i=1∑n−1P(i)
对于每个P(i)P(i)P(i)我们固定到左边第一个比它大的数的路径然后剩下的随便选就可以用组合数来计算方案,而左右两边都减去后要容斥加上两边都被删除的方案数。
时间复杂度O(n2logP)O(n^2\log P)O(n2logP)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll XJQ=998244353,N=60;
ll n,a[N],fac[N],inv[N],ans;
ll power(ll x,ll b){ll ans=1;while(b){if(b&1)ans=ans*x%XJQ;x=x*x%XJQ;b>>=1;}return ans;
}
ll C(ll x,ll y)
{if(x<0||y<0||x<y)return 0;return fac[x]*inv[y]%XJQ*inv[x-y]%XJQ;
}
int main()
{scanf("%lld",&n);a[0]=a[n+1]=2147483647;fac[0]=inv[0]=1;for(ll i=1;i<=n;i++){scanf("%lld",&a[i]);fac[i]=fac[i-1]*i%XJQ;inv[i]=inv[i-1]*power(i,XJQ-2)%XJQ;}for(ll p=1;p<=n;p++){ll l=p-1,r=p+1;ans=0;while(a[l]<a[p])l--;while(a[r]<a[p])r++;for(ll i=1;i<n;i++){ll tmp=0,d1=p-l,d2=r-p;if(l)tmp=(tmp+C(n-1-d1,i-d1))%XJQ;if(r!=n+1)tmp=(tmp+C(n-1-d2,i-d2))%XJQ;if(r!=n+1&&l)tmp=(tmp-C(n-1-d1-d2,i-d1-d2)+XJQ)%XJQ;ans=(ans+1-tmp*power(C(n-1,i),XJQ-2)%XJQ+XJQ)%XJQ;}printf("%lld ",ans);}
}