[HDU-6848] Expectation
(2020多校7T5) (dp)
比赛时疯狂脑抽写了3个小时祭
考虑计算每条 \(x_i\rightarrow
x_{i+1}\) 的边被在所有情况下被经过的次数总和
令 \(dp[i][j]\) 为有 \(i\) 个球时, \(x_j\rightarrow x_{j+1}\)
这段被经过的次数总和( \(j\leq 2i\)
)
考虑转移,对于 \(dp[i]\)
,枚举每个球向左或者右走,发现把两边的部分拉拢过来后,合并形成一条包含了原先
\(3\) 条边的新边,变成了 \(i-1\) 阶的子问题
画图理解,发现 \(j\) 这条边,在
\(i-1\) 阶的子问题上对应的编号只可能是
\(j,j-1,j-2\)
视选择了 \(j\)
这条边为将边一端的球滚进另一端的洞里
那么对于任意一条编号为 \(j\)
的边
\(j\) 变为编号为 \(j-2\) 的情况为选择了编号 \([1,j-1]\) 范围内的边
\(j\) 变为编号为 \(j-1\) 的情况为选择了编号为 \(j\) 的边
\(j\) 变为编号为 \(j\) 的情况为选择了编号为 \([j+1,2i]\) 的边
对于 \(j\)
在子问题中被访问的次数可以直接 \(O(1)\)
继承过来
同时,考虑当第一次就选了 \(j\)
时,后面的操作随意,即加上 \((i-1)!\cdot
2^{i-1}\)
于是得到一个 \(O(n^2)\) 的 \(dp\) 预处理
而对于每个询问,求解 \(n\)
阶的答案复杂度为 \(O(n)\)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <bits/stdc++.h> using namespace std;typedef long long ll;#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i) char IO;template <class T =int > T rd (){ T s=0 ; int f=0 ; while (!isdigit (IO=getchar ())) if (IO=='-' ) f=1 ; do s=(s<<1 )+(s<<3 )+(IO^'0' ); while (isdigit (IO=getchar ())); return f?-s:s; } const int N=3010 ,P=998244353 ;int n;int dp[N][N*2 ],Fac[N];ll qpow (ll x,ll k=P-2 ) { ll res=1 ; for (;k;k>>=1 ,x=x*x%P) if (k&1 ) res=res*x%P; return res; } int main () { rep (i,Fac[0 ]=1 ,N-1 ) Fac[i]=1ll *i*Fac[i-1 ]%P; dp[1 ][1 ]=dp[1 ][2 ]=1 ; int t=1 ; rep (i,2 ,N-1 ) { t=1ll *t*(i-1 )*2 %P; rep (j,1 ,i*2 ) { dp[i][j]=(1ll *(i*2 -j)*dp[i-1 ][j]+1ll *dp[i-1 ][j-1 ]+1ll *(j-1 )*(j>=2 ?dp[i-1 ][j-2 ]:0 )+t)%P; } } rep (kase,1 ,rd ()) { n=rd (); int ans=0 ,x=rd (); rep (i,1 ,n*2 ) { int y=rd (); ans=(ans+1ll *(y-x)*dp[n][i])%P; x=y; } ans=ans*qpow ((P+1 )/2 ,n)%P*qpow (Fac[n])%P; printf ("%d\n" ,ans); } }