AT4436 [AGC028B] Removing Blocks
Solution
我们直接考虑对于第 $ i $ 个数,他所能做的总贡献是多少。
很显然,他所能做的贡献是与他删除时的区间有关的,我们考虑枚举那个区间,记为 $ [l,r] $ 。
那么考虑如何算贡献,我们发现,如果 $ [l,r] $ 这个区间是要被算到的,那么 $ l-1 $ 和 $ r+1 $ 一定要在他们之间被删掉,因为 $ i $ 被算贡献时这个区间还存在,所以剩余 $ r-l $ 个数一定是在 $ i $ 之后删的,知道了这个,就可以算方案数了,我们记 $ l-1 $ 和 $ r+1 $ 存在的数的数目为 $ tot $ ,那么有方案数为 $ T $ 。
\[T=\frac{n!}{(r-l+1+tot)!}\times(r-l)!\times tot!
\]
具体解释上文已有,而这段区间所能贡献的价值就是方案数与区间重量的乘积,为 $ T\times (pre[r]-pre[l-1]) $ 。
而我们其实可以观察到,$ i $ 与总价值的表达式并无关系,所以可以省去一重循环,则区间 $ [l,r] $ 的真正价值为 $ T\times (pre[r]-pre[l-1])\times(r-l+1) $ 。
然而这样子还是不够快,所以我们考虑加速,而如何加速?
我们发现,$ tot $ 不为 $ 2 $ 的情况仅有 $ l=1 $ 和 $ r=n $ ,那我们可以把这一部分的贡献先算出来,然后剩下的情况就是 $ tot=2 $ 了,然后我们考虑枚举长度,就可以先算出 $ T $ 的值,但接下来还有一个问题,那么接下来就是这个值的快速求值:
\[\sum_{l=2}^{n-1-len+1} pre[l+len-1]-pre[l-1]
\]
不会真的有人不会吧。
Code
long long sum,len,g[1001010],down,fre,lim,tot,now,a[1001010],MOD,pre[1001010],ans,fac[1001010],ifac[1001010],lft,rit,val,n,i;
long long C(long long n,long long m)
{
if ((n<0)||(m<0)||(n<m)) return 0;
return fac[n]*ifac[n-m] % MOD*ifac[m] % MOD;
}
int main()
{
//ios::sync_with_stdio(0);cin.tie();cout.tie();
n=read();MOD=1e9+7;
fac[0]=1;
for (i=1;i<=n;i++) fac[i]=fac[i-1]*i % MOD;
ifac[n]=pow(fac[n],MOD-2,MOD);
for (i=n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1) % MOD;
for (i=1;i<=n;i++) a[i]=read(),pre[i]=pre[i-1]+a[i];
for (lft=1;lft<=n;lft++)
{
down=lft;
if (lft!=1) down=n;
for (rit=down;rit<=n;rit++)
{
val=(pre[rit]-pre[lft-1]) % MOD;
//lft-1 & rit+1 is very important ,有 rit-lft 个数一定要在 i的后面,tot个数一定要在i的前面。
tot=0;
if (lft-1>=1) tot++;
if (rit+1<=n) tot++;
fre=n-1-tot-(rit-lft);
if (fre<0) continue;
lim=tot+1+rit-lft;//rit++,len++,now
//a b c d
ans=ans+(fac[n]*val % MOD*ifac[lim] % MOD*fac[tot] % MOD*fac[rit-lft] % MOD*(rit-lft+1) % MOD);
//abcd->(a+a[rit])*(b/(len+tot+1))*(c*len))*(d+1)
//(a+a[rit])*(d+1)=ad+a[rit]*d+
//我们考虑调换顺序
ans%=MOD;
}
}
for (len=1;len<=n-2;len++)
{
//val:fac[len-1]*len*fac[n]
//((lft>1)&&(rit<n))
//val:fac[len-1]*len*fac[n]*ifac[len]*fac[0]
lft=2+len-1;rit=n-1-len+1;
sum+=pre[rit]-pre[lft-1];
sum%=MOD;
g[len]=sum;
if (len>(n-1)/2) g[len]=g[n-2-len+1];
now=fac[len-1]*len % MOD*fac[n] % MOD*ifac[len+2] % MOD*fac[2] % MOD*g[len] % MOD;
ans=(ans+now) % MOD;
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号