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;
}
posted @ 2021-09-23 13:08  OIer_Albedo  阅读(82)  评论(0)    收藏  举报