kmjp's blog

競技プログラミング参加記です

yukicoder : No.2457 Stampaholic (Easy)

Hard解いたらEasyも同じコードで通るかと思ったら、微妙に制限が違った。
https://yukicoder.me/problems/no/2457

問題

整数H,W,N,Kが与えられる。
H*Wのグリッドがあり、初期状態で各マスは白色である。
ここに、K*Kのサイズの黒いスタンプを、マス目に沿ってどこかの領域に等確率で押すことをN回繰り返す。
最終的な黒マス数の期待値を求めよ。

解法

あるマスについて、スタンプの押し方のバリエーションがS通りあり、そのうちそのマスを黒くするような塗り方がM通りあれば、そのマスが黒い確率P(M)は
 \displaystyle P(M)=1-(\frac{S-M}{S})^Nである。

マスを黒くするような塗り方がM通りであるマスの個数とcnt(M)とすると、求めるのは
 \displaystyle \sum_M P(M)cnt(M)
である。Mは高々K^2通りなので、各Mに対しcnt(M)を求めればよい。

あるマスに対し、そのマスを黒くできるようなスタンプの位置の行の候補がh個ある行をch(h)、列の候補がh個ある列をcw(w)とすると、
 cnt(M)=\sum_{h \times w = M} ch(h)cw(w)である。
ch(h)、cw(w)はO(1)で計算できるので、h,wを1~Kまで総当たりすればcnt(M)を計算できる。

const int mo=998244353;
ll modpow(ll a, ll n = mo-2) {
	ll r=1; a%=mo;
	while(n) r=r*((n%2)?a:1)%mo,a=a*a%mo,n>>=1;
	return r;
}


ll comb(ll N_, ll C_) {
	const int NUM_=2400001;
	static ll fact[NUM_+1],factr[NUM_+1],inv[NUM_+1];
	if (fact[0]==0) {
		inv[1]=fact[0]=factr[0]=1;
		for (int i=2;i<=NUM_;++i) inv[i] = inv[mo % i] * (mo - mo / i) % mo;
		for (int i=1;i<=NUM_;++i) fact[i]=fact[i-1]*i%mo, factr[i]=factr[i-1]*inv[i]%mo;
	}
	if(C_<0 || C_>N_) return 0;
	return factr[C_]*fact[N_]%mo*factr[N_-C_]%mo;
}

int H,W,N,K;


void solve() {
	int i,j,k,l,r,x,y; string s;
	
	
	cin>>H>>W>>N>>K;
	
	vector<ll> Hs(K+1),Ws(K+1);
	
	int numH=0,Hma=0;;
	int numW=0,Wma=0;;
	if(2*K<=H) {
		for(i=1;i<K;i++) Hs[i]=2;
		Hs[K]=H-2*K+2;
	}
	else {
		for(i=1;i<H-K+1;i++) Hs[i]=2;
		Hs[H-K+1]=2*K-H;
	}
	if(2*K<=W) {
		for(i=1;i<K;i++) Ws[i]=2;
		Ws[K]=W-2*K+2;
	}
	else {
		for(i=1;i<W-K+1;i++) Ws[i]=2;
		Ws[W-K+1]=2*K-W;
	}
	
	ll ret=0;
	ll L=1LL*(H-K+1)*(W-K+1)%mo;
	ll rL=modpow(L);
	
	for(x=1;x<=K;x++) for(y=1;y<=K;y++) {
		ret+=Hs[x]*Ws[y]%mo*(1-modpow((L-x*y)*rL,N))%mo;
	}
	
	ret=(ret%mo+mo)%mo;
	cout<<ret<<endl;
	
}

まとめ

色々解法ありそう。