CF1151F Sonya and Informatics

一、题目

点此看题

二、解法

这道题必须要考虑到终止状态,由于题目给的是 0 / 1 0/1 0/1串,所以最后的状态一定是前面都是 0 0 0,后面全是 1 1 1。设 m m m 0 0 0的个数, d p [ i ] dp[i] dp[i]表示前 m m m个数中有 i i i个数是 0 0 0的概率,从 d p [ i ] dp[i] dp[i]转移出去:

  • 增加一个 0 0 0,从前面选一个 1 1 1,后面选一个 0 0 0 d p [ i ] × ( m − i ) 2 dp[i]\times(m-i)^2 dp[i]×(mi)2
  • 减少一个 0 0 0,从前面选一个 0 0 0,后面选一个 1 1 1 d p [ i ] × i × ( n − 2 m + i ) dp[i]\times i\times(n-2m+i) dp[i]×i×(n2m+i)
  • 不增不减,只选前 / / /后,选前后 1 / 0 1/0 1/0 C ( m , 2 ) + C ( n − m , 2 ) + i ( m − i ) + ( m − i ) ( n − 2 m + i ) C(m,2)+C(n-m,2)+i(m-i)+(m-i)(n-2m+i) C(m,2)+C(nm,2)+i(mi)+(mi)(n2m+i)

k k k很大,但我们可以做矩阵加速,注意设置矩阵的时候是从 i + 1 / i − 1 i+1/i-1 i+1/i1转移到 i i i,所以要把上文的 i i i替换成 i + 1 / i − 1 i+1/i-1 i+1/i1,具体可以参考代码。

#include <cstdio>
#include <cstring>
const int M = 105;
const int MOD = 1e9+7;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,t,t1,t2,a[M];
struct Matrix
{
	int n,m,a[M][M];
	Matrix() {n=m=0;memset(a,0,sizeof a);}
	void clear() {memset(a,0,sizeof a);}
	Matrix operator * (const Matrix &b) const
	{
		Matrix r;
		r.n=n;r.m=b.m;
		for(int i=0;i<=n;i++)
			for(int j=0;j<=m;j++)
				for(int k=0;k<=b.m;k++)
					r.a[i][k]=(r.a[i][k]+a[i][j]*b.a[j][k])%MOD;
		return r;
	}
	void print()
	{
		for(int i=1;i<=n;i++,puts(""))
			for(int j=1;j<=m;j++)
				printf("%lld ",a[i][j]);
	}
}A,F;
Matrix qkpow(Matrix a,int b)
{
	Matrix r;
	r.n=r.m=a.n;
	for(int i=1;i<=a.n;i++)
		r.a[i][i]=1;
	while(b>0)
	{
		if(b&1) r=r*a;
		a=a*a;
		b>>=1;
	}
	return r;
}
int fast(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=r*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return r;
}
signed main()
{
	n=read();k=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		if(!a[i]) m++;
	}
	for(int i=1;i<=m;i++)
		if(!a[i]) t++;
	A.n=A.m=F.n=m;F.m=1;
	if(m==0 || m==n)
	{
		puts("1");
		return 0;
	}
	t1=m*(m-1)/2;t2=(n-m)*(n-m-1)/2;
	int inv=fast(n*(n-1)/2,MOD-2);
	for(int i=0;i<=m;i++)
	{
		if(i>0) A.a[i][i-1]=(m-(i-1))*(m-(i-1));
		A.a[i][i]=t1+t2+i*(m-i)+(m-i)*(n-2*m+i);
		if(i<m) A.a[i][i+1]=(i+1)*(n-2*m+(i+1));
	}
	F.a[t][1]=1;
	A=qkpow(A,k);F=A*F;
	printf("%lld\n",F.a[m][1]*fast(inv,k)%MOD);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值