Codeforces 369(#216 Div2 only) D. Valera and Fools

問題

n人がいて、それぞれ番号が1〜n番である。
全員がピストルとk発の弾をもっていて、k回つぎのことを行う。


自分以外の生存者のうち、番号がもっとも小さい人に向けて同時に銃を撃つ。
i番目の人が撃った銃は、p[i]%の確率で当たる。弾が当たった人は死ぬ。


k回以内(0回も含む)のラウンドが終了した時点における生存者の組み合わせとして、
ありえるのは何通りか、求めよ。

制約条件

n, k≦3000
0≦p[i]≦100

方針

全員の弾は、番号の一番小さい人を狙って、
一番小さい人だけが、二番目に番号の小さい人を狙う。


なので、生き残っている人は、
(ここまで全員死亡)l番目の人、(この間は全員死亡)r番目の人、r+1番目の人…n番目の人
という形になるしかない。


なので、dp[l][r]をその状態に遷移するのに必要なラウンドの回数の最小値というdpをすればいい。
ちょうどk回のラウンドが終わった後の状態と誤読して、なんか面倒なことをやってしまった。


lが生き残るには、r番以降の人にp[i]が100でない人がいる必要がある。
lが死ぬには、r番以降の人にp[i]が0でない人がいる必要がある。


rが生き残るにはp[l]が100でない必要がある。
rが死ぬにはp[i]が0でない必要がある。

ソースコード

const int N = 3010;
int n, k, p[N], sum[N], s[N];

bool dp[N][N][2];
set<pi> ans;

void rec(int l, int r, int f, int rest){
	
	l = min(l, n);
	r = min(r, n);
	
	if(dp[l][r][f]) return;
	dp[l][r][f] = 1;
	
	if(sum[n] - sum[r] == 0) f = 1;
	ans.insert(mp(l, r));
	if(rest == 0) return;
	
	if(p[l] > 0 && s[n] - s[r] > 0) rec(r + 1, r + 2, f, rest - 1); //l, r死ぬ
	if(p[l] > 0 && sum[n] - sum[r] == 0) rec(l, r + 1, f, rest - 1); //r死ぬ
	if(p[l] < 100 && s[n] - s[r] > 0) rec(r, r + 1, f, rest - 1); //l死ぬ
}

int main(){
	
	cin >> n >> k;
	rep(i, n) cin >> p[i];
	
	rep(i, n){
		sum[i + 1] = sum[i] + (p[i] == 100);
		s[i + 1] = s[i] + (p[i] > 0);
	}
	
	rec(0, 1, 0, k);
	cout << ans.size() << endl;
	
	return 0;
}