luogu 6046 纯粹容器 期望dp

LINK:纯粹容器

一道比较不错的期望题目。

关键找到计算答案的方法。

容易发现对于每个点单独计算答案会好处理一点。

暴力枚举在第k轮结束统计情况 然后最后除以总方案数即可。

考虑在第k轮的时候结束 我们要求出其所有的方案。

首先一个点在第k轮结束必须要有一个点在第k轮和它相遇。

如果暴力枚举这个点的话可能有不合法的方案 也不太容易进行计算。

容易发现击败某个点的点在左边或者右边 分别设为l,r.

考虑最后一定是 il或者ir这段点都没了。

如果l/r被击败了 也不影响解决 所以我们只关心il或者ir这两端所造成的方案数。

考虑如果是被左边击败了 设为calc(i-l,j).含义表示这i-l轮在前j轮中出现了。

仔细分析这个东西的含义 其实i可能在前j轮中的任意一轮都出现了 但是此时我们强制其在第j轮出现 那么减掉出现在1~j-1轮的方案数即可。

考虑calc(i-l,j)怎么计算:我的计算方法是 C(j,i-l)fac[i]C(n-1-i,j-i)fac[j-i]fac[n-1-j].

值得一提的是 如果同时可以被两边击败那么方案为 calc(i-l,j)+calc(r-i,j)不难发现有重复的地方发生。

即此时已经被左边击败了可是计算的确是右边的方案数反之同理 所以要减掉两边被同时击败的方案数C(r-l,j).

const ll MAXN=300010,G=3;
ll n;
ll fac[MAXN],inv[MAXN],a[MAXN],w[MAXN];
inline ll ksm(ll b,ll p)
{
	ll cnt=1;
	while(p)
	{
		if(p&1)cnt=(ll)cnt*b%mod;
		b=(ll)b*b%mod;
		p=p>>1;
	}
	return cnt;
}
inline ll C(ll a,ll b){if(a<b)return 0;return fac[a]*inv[b]%mod*inv[a-b]%mod;}
inline ll calc(ll i,ll j){if(j<i)return 0;return C(j,i)*C(n-1-i,j-i)%mod*fac[i]%mod*fac[j-i]%mod;}
signed main()
{
	freopen("1.in","r",stdin);
	get(n);fac[0]=1;
	rep(1,n,i)get(a[i]),fac[i]=fac[i-1]*i%mod;
	inv[n]=ksm(fac[n],mod-2);
	fep(n-1,0,i)inv[i]=inv[i+1]*(i+1)%mod;
	//cout<<calc(1,2)<<endl;
	rep(1,n,i)
	{
		ll l=0,r=0,ans=0;
		fep(i-1,1,j)if(a[j]>a[i]){l=j;break;}
		rep(i+1,n,j)if(a[j]>a[i]){r=j;break;}
		if(!l&&!r){printf("%d ",n-1);continue;}
		int las=0;
		rep(1,n-1,j)
		{
			if(!l)
			{
				ans=(ans+((calc(r-i,j)*fac[n-1-j])-las)%mod*(j-1))%mod;
				las=(calc(r-i,j)*fac[n-1-j])%mod;
				//putl(calc(r-i,j));//putl(calc(r-i,j-1));
			}
			if(!r)
			{
				ans=(ans+(calc(i-l,j)*fac[n-1-j]-las)%mod*(j-1))%mod;
				las=calc(i-l,j)*fac[n-1-j]%mod;
			}
			if(l&&r)
			{
				ans=(ans+((calc(i-l,j)+calc(r-i,j)-calc(r-l,j))*fac[n-1-j]-las)%mod*(j-1))%mod;
				las=((calc(i-l,j)+calc(r-i,j)-calc(r-l,j))*fac[n-1-j])%mod;
			}
		}
		ans=ans*inv[n-1]%mod;
		printf("%lld ",(ans+mod)%mod);
	}
	return 0;
}
上一篇:AtCoder Beginner Contest 171 F 另类题解


下一篇:#排列组合,容斥#洛谷 5684 [CSPJX2019]非回文串