kmjp's blog

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

Codeforces #602 Div1 D2. Wrong Answer on test 233

実際テストケース233で失敗したら嫌な気分になるよね。
http://codeforces.com/contest/1261/problem/D2

問題

K択問題がN個あるとし、その解が与えられる。
この回答を1つrotateした場合を考える。

K択問題N個の解答はK^N通り考えられるが、正答がrotateした方が正答数が増えるのは何通りか。

解法

rotateしてもしなくても解が変わらない部分は無視し、そうでない場所がD問あったとする。
これらD問に対し取りえる解は以下のいずれかである。

  • rotate前の正答
  • rotate後の正答
  • いずれでも正答でない残り(K-2)通り

D問中前者のいずれかを取る問題がi問だとする。
i問で後者を選ぶのはfloor(i/2)より多ければよいので、それらを数え上げればよい。

int N,K;
int H[202020];
const ll mo=998244353;

ll comb(ll N_, ll C_) {
	const int NUM_=400001;
	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;
}

ll from[4020];
ll to[4020];
ll P[202020];
ll P2[202020];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>K;
	FOR(i,N) {
		cin>>H[i];
		H[i]--;
	}
	H[N]=H[0];
	
	P[0]=1;
	P2[0]=1;
	FOR(i,N+1) {
		P[i+1]=P[i]*(K-2)%mo;
		P2[i+1]=P2[i]*2%mo;
	}
	
	int same=0,dif=0;
	FOR(i,N) {
		if(H[i]==H[i+1]) same++;
		else dif++;
	}
	ll ret=0;
	
	for(i=0;i<=dif;i++) {
		int either=dif-i;
		
		ll pat;
		if(either%2==1) {
			pat=P2[either-1];
		}
		else {
			pat=P2[either]+mo-comb(either,either/2);
			pat=pat%mo*((mo+1)/2)%mo;
		}
		
		(ret+=P[i]*pat%mo*comb(dif,i))%=mo;
	}
	
	
	ret%=mo;
	FOR(i,same) ret=ret*K%mo;
	
	cout<<ret%mo<<endl;
}

まとめ

Div1のDとしては簡単な気がする。