传送门
在历史上,有这样一个国家,这个国家由 n n n 个村庄组成。第 i i i 个村庄有 a i a_i ai 个人。由于每个村庄都实行严格的计划生育,在整个历史的过程中,每个村庄的人数都没有变化。
这 n n n 个村庄线性排列在一条线上,并且只有相邻的村庄能互通。也就是说,对于所有的 1 ≤ i < n 1 \le i<n 1≤i<n ,村庄 i i i 和 i + 1 i+1 i+1 存在一条边。除了这些边外,其他村庄之间不能同行。换句话说,对于 i < j i<j i<j ,如果想要从 i i i 走到 j j j ,那么必须经过 i , i + 1 ⋯ , j − 1 , j i,i+1\cdots, j-1,j i,i+1⋯,j−1,j 这些村庄。
村庄之间经常会闹矛盾,小矛盾可能不久后会解决,但是大矛盾就危险了,如果村庄 i , i + 1 i,i+1 i,i+1 之间闹大矛盾,那么这两个村庄就会断绝来往,并且破坏之间
的道路。这样一来,村庄之间就不连通了。
一个国家一旦不连通是非常致命的,所以这个国家的领导者会放弃这个国家(???),并且在剩下的两个连通块中选择村庄数较多的一个,把它作为自己的新国家。如果左右两个连通块的村庄数相同,那么他会选择右边的(村庄编号大的)连通块。
当然,另一个被放弃的连通块也不会消失,他们会举行一次选举,选出新的领导者。这次选举每个人都有选举权,所以每个人都有一张选票。因此,这次选举需要制作连通块内村庄人数之和的选票。在经过这样的操作后,一个国家就会分裂成两个国家了。然而,村庄之间的矛盾并不会消除,只要两个村庄还在一个国家中,之后还
会有村庄之间闹大矛盾。因此,这些村庄还会变成三个,四个,直到最后变成 n n n个国家。
不过值得庆幸的是,发生大矛盾的概率很低,所以在历史的长河中,需要隔很久才会发生一次国家的分裂,并且每条边分裂的概率相等。
尽管如此,最后这些村庄还是分裂成了 n n n 个国家。
“听完这个故事,你有什么感想? ”小 H 说。
“这个故事有任何寓意吗?不对,这是个故事吗? ”宫水三叶说。
“是吗?那我问你,从一个国家到 n n n 个国家,期望制作了多少张选票?当然,
我问的是对 998244353 998244353 998244353 取模后的值。 ”小 H 说。
给定一个长度为 n n n 的链,第 i i i 个点的点权为 a i a_i ai ,其中对于所有的 1 ≤ i < n 1 \le i<n 1≤i<n ,点 i i i 和点 i + 1 i + 1 i+1 相连。总共有 n − 1 n − 1 n−1 次操作,每次操作等概率会选择一条还未断开的边,然后把它断开。此次操作的权值为剩下两部分中点数较少的部分的点权之和,如果两部分点数相等,权值为点编号较小
的那部分的点权之和。
求 n − 1 n − 1 n−1 次操作后权值之和的期望值对 998244353 998244353 998244353 取模后的结果。
第一行一个整数 n n n 。
第二行 n n n 个整数,第 i i i 个数表示 a i a_i ai ,即第 i i i 个村庄的人数。
输出一行,表示期望的选票数量对 998244353 998244353 998244353 取模后的结果。
样例输入1
3
1 2 3
样例输出1
499122180
样例解释1
如果先断开 (1,2) ,再断开 (2,3) ,那么权值为 1 + 2 = 3。
如果先断开 (2,3) ,再断开 (1,2) ,那么权值为 3 + 1 = 4。
因此期望值为 3 + 4 2 = 7 2 \frac{3+4}{2}=\frac{7}{2} 23+4=27
对于所有数据,满足 1 ≤ n ≤ 2 × 1 0 6 , 0 ≤ s i ≤ 1 0 9 1 \le n \le 2 \times 10^6, 0 \le s_i \le 10^9 1≤n≤2×106,0≤si≤109
子任务编号 | n n n | 特殊性质 | 分值 |
---|---|---|---|
1 | $ \le 10$ | - | 5 |
2 | $ \le 20$ | - | 5 |
3 | $ \le 3000$ | - | 20 |
4 | $ \le 2000$ | - | 20 |
5 | $ \le 2000$ | S i = 1 S_i=1 Si=1 | 10 |
6 | $ \le 5 \times 10^4$ | S i = 1 S_i=1 Si=1 | 10 |
7 | $ \le 2 \times 10^6$ | S i = 1 S_i=1 Si=1 | 10 |
8 | $ \le 2 \times 10^6$ | - | 20 |
题解:
首先,一眼 D P DP DP。
首先想到区间 D P DP DP。转移显然。
但 n 3 n^3 n3 也显然过不了。
这时发现部分分 s i = 1 s_i=1 si=1,发现同样长的区间必然相同。
于是我们直接 f [ i ] f[i] f[i] 表示长度为 i i i 的区间的答案。
转移即 f [ i ] ( i − 1 ) ! = 2 ( ∑ j f [ j ] j ! ) + 2 ( ∑ j ⌊ i − 1 2 ⌋ j ) − ( ( i + 1 ) / 2 [ 2 ∣ i + 1 ] ) \frac{f[i]}{(i-1)!}=2(\sum_j \frac{f[j]}{j!})+2(\sum_j^{\left\lfloor\dfrac{i-1}{2}\right\rfloor}j)-((i+1)/2[2|i+1]) (i−1)!f[i]=2(∑jj!f[j])+2(∑j⌊2i−1⌋j)−((i+1)/2[2∣i+1])
前缀和优化即可。
#include<bits/stdc++.h>
#define N 2000006
typedef long long ll;
using namespace std;
const ll mod=998244353;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){
x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll jc[N],jcinv[N];
inline ll power(ll x,int c){
ll now=1;
while(c){
if(c&1)now=now*x%mod;
x=x*x%mod;c>>=1;
}
return now;
}
ll f[N],sum[N],a[N];
inline void init(int n){
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=jc[i-1]*1ll*i%mod,sum[i]=(sum[i-1]+i)%mod;
jcinv[n]=power(jc[n],mod-2);
for(int i=n-1;i+1;--i)jcinv[i]=jcinv[i+1]*1ll*(i+1)%mod;
ll sumf=0;
for(int i=1;i<=n;++i){
f[i]=2*sumf+2*sum[(i-1)/2+1];
if((i+1)%2==0)f[i]-=(i+1)/2;
f[i]=f[i]%mod*jc[i-1]%mod;
sumf=(sumf+f[i]*jcinv[i]%mod)%mod;
}
}
ll g[305][305],suma[N];
inline ll calc(int l,int r,int x,int y){
if(r-l+1<=y-x+1)return suma[r]-suma[l-1];
else return suma[y]-suma[x-1];
}
int main(){
int n=read(),flag=1;
for(int i=1;i<=n;++i)a[i]=read(),flag&=(a[i]==1);
for(int i=1;i<=n;++i)suma[i]=suma[i-1]+a[i];
init(n);
if(flag){
ll ans=f[n-1]*jcinv[n-1]%mod;
printf("%lld\n",(ans+mod)%mod);
return 0;
}
for(int len=2;len<=n;++len){
for(int i=1;i<=n-len+1;++i){
int j=i+len-1;
for(int k=i;k<j;++k){
g[i][j]=(g[i][j]+g[i][k]+g[k+1][j]+calc(i,k,k+1,j)%mod)%mod;
}
g[i][j]=g[i][j]*jcinv[j-i]%mod*jc[j-i-1]%mod;
}
}
//for(int i=1;i<=n;++i)cout<<jcinv[i]<<" ";cout<<endl;
ll ans=g[1][n];
printf("%lld\n",(ans+mod)%mod);
return 0;
}
/*
10
5 9 8 6 3 2 1 4 7 10
*/
但是很明显 D P DP DP 没有前途。
所以考虑计数。
发现对于一段区间 [ L , R ] [L,R] [L,R],设 l e n len len 为长度。假设作为左区间,那么只需要先删 [ L − 1 , L ] [L-1,L] [L−1,L]的边,再删 [ R , R + 1 ] [R,R+1] [R,R+1],然后删 [ L , R ] [L,R] [L,R] 和 [ R + 1 , R + 1 + l e n ] [R+1,R+1+len] [R+1,R+1+len],其他边任意就可以,于是系数为 C ( n − 1 , 2 ∗ l e n + 2 ) × ( 2 ∗ l e n ) ! × ( n − 1 − ( 2 ∗ l e n − 2 ) ) ! C(n-1,2*len+2)\times (2*len)! \times (n-1-(2*len-2))! C(n−1,2∗len+2)×(2∗len)!×(n−1−(2∗len−2))!。
时间复杂度 O ( n 2 ) O(n^2) O(n2)
#include<bits/stdc++.h>
#define N 2000006
typedef long long ll;
using namespace std;
const ll mod=998244353;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){
x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll jc[N],jcinv[N];
inline ll power(ll x,int c){
ll now=1;
while(c){
if(c&1)now=now*x%mod;
x=x*x%mod;c>>=1;
}
return now;
}
inline void init(int n){
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=jc[i-1]*1ll*i%mod;
jcinv[n]=power(jc[n],mod-2);
for(int i=n-1;i+1;--i)jcinv[i]=jcinv[i+1]*1ll*(i+1)%mod;
}
inline ll C(int x,int y){
if(x<y||y<0)return 0;
return jc[x]*jcinv[y]%mod*jcinv[x-y]%mod;
}
ll sum[N],a[N];
int main(){
int n=read();
for(int i=1;i<=n;++i)a[i]=read(),sum[i]=(sum[i-1]+a[i])%mod;
init(n+3);
ll ans=0;
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j){
int len=j-i;ll now;
if(j+1+len>n)continue;
if(i==1){
now=C(n-1,2*len+1)*jc[2*len]%mod*jc[n-1-2*len-1]%mod;
}else{
now=C(n-1,2*len+2)*jc[2*len]%mod*jc[n-1-2*len-2]%mod;
}
ans=(ans+now*(sum[j]-sum[i-1])%mod)%mod;
}
}
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j){
int len=j-i;ll now;
if(i-2-len<1)continue;
if(j==n){
now=C(n-1,2*len+2)*jc[2*len+1]%mod*jc[n-1-2*len-2]%mod;
}else{
now=C(n-1,2*len+3)*jc[2*len+1]%mod*jc[n-1-2*len-3]%mod;
}
ans=(ans+now*(sum[j]-sum[i-1])%mod)%mod;
}
}
ans=ans*jcinv[n-1]%mod;
printf("%lld\n",(ans+mod)%mod);
return 0;
}
又发现同样长度的区间系数相同,那么只需要对 a i a_i ai 的前缀和 s u m i sum_i sumi 再取前缀和就可以维护了。
注意分类 [ 1 , R ] [1,R] [1,R] 的情况,系数不一样。
右区间同理。
时间复杂度 O ( n ) O(n) O(n)
#include<bits/stdc++.h>
#define N 2000006
typedef long long ll;
using namespace std;
const ll mod=998244353;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){
x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll jc[N],jcinv[N];
inline ll power(ll x,int c){
ll now=1;
while(c){
if(c&1)now=now*x%mod;
x=x*x%mod;c>>=1;
}
return now;
}
inline void init(int n){
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=jc[i-1]*1ll*i%mod;
jcinv[n]=power(jc[n],mod-2);
for(int i=n-1;i+1;--i)jcinv[i]=jcinv[i+1]*1ll*(i+1)%mod;
}
inline ll C(int x,int y){
if(x<y||y<0)return 0;
return jc[x]*jcinv[y]%mod*jcinv[x-y]%mod;
}
ll sum[N],a[N],tt[N],ccl[N];
int main(){
int n=read();
for(int i=1;i<=n;++i)a[i]=read(),sum[i]=(sum[i-1]+a[i])%mod,tt[i]=(sum[i]+tt[i-1])%mod;
init(n+3);
ll ans=0;
for(int len=0;len<=(n-2)/2;++len){
ll pre=ans,now;
now=((tt[n-len-1]-tt[2+len-1])-(tt[n-2*len-1-1]-tt[1-1]))%mod*C(n-1,2*len+2)%mod*jc[2*len]%mod*jc[n-1-2*len-2]%mod;
ans=(ans+now)%mod;
now=C(n-1,2*len+1)*jc[2*len]%mod*jc[n-1-2*len-1]%mod*(sum[1+len]-sum[0])%mod;
ans=(ans+now)%mod;
}
ans=(ans+mod)%mod;
for(int len=0;len<=(n-3)/2;++len){
ll pre=ans,now;
now=((tt[n-1]-tt[2*len+3-1])-(tt[n-1-len-1]-tt[len+3-1-1]))%mod*C(n-1,2*len+3)%mod*jc[2*len+1]%mod*jc[n-1-2*len-3]%mod;
ans=(ans+now)%mod;
now=C(n-1,2*len+2)*jc[2*len+1]%mod*jc[n-1-2*len-2]%mod*(sum[n]-sum[n-len-1])%mod;
ans=(ans+now)%mod;
}
ans=(ans+mod)%mod;
ans=ans*jcinv[n-1]%mod;
printf("%lld\n",(ans+mod)%mod);
return 0;
}