kmjp's blog

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

yukicoder : No.1780 [Cherry Anniversary] 真冬に咲く26の櫻の木

入力欄にちょっと驚いた。
https://yukicoder.me/problems/no/1780

問題

A~Zの26個の木があり、それぞれ初期状態の色が与えられる。
ここでN種類の操作が与えられる。
各操作は、名称と2種類の色とエネルギーからなる。

各木に対し、以下の処理を行うことを考える。

  • 操作のうち、各木に対応する文字を名称に含む操作を1つ選ぶ。木の色が操作に関する2色のうち片方であれば、木の色をもう片方に入れ替え、操作に対応したエネルギーを得る。
  • 各木に対して行える処理回数は、木毎に決められている。

各木の色をそろえる場合、得られる総エネルギーの最大値を求めよ。

解法

木ごとに、各色にする場合に得られる総エネルギーを求めよう。
操作の種類は多いが、色の種類が少ないので、ダブリングの要領で、初期状態の色から指定の色に指定回数の処理で遷移する場合のエネルギーの総和を求めることができる。
この際、処理回数の上限は与えられているものの、それより少ない回数でも良い。

そこで以下では、色の数をCとすると、以下では処理回数を0~(C-1)と、(処理回数上限-C)~(処理回数上限)あたりについて総当たりした。

int C[26],K[26];
int N;
string S[202020];
ll A[202020],B[202020],E[202020];
ll dp[16][16][31];
ll ret[26][16];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	FOR(i,26) {
		cin>>C[i];
		C[i]--;
	}
	FOR(i,26) {
		cin>>K[i];
	}
	cin>>N;
	FOR(i,N) {
		cin>>S[i]>>A[i]>>B[i]>>E[i];
		FORR(c,S[i]) c-='A';
		A[i]--;
		B[i]--;
	}
	
	FOR(i,26) {
		FOR(x,16) FOR(y,16) dp[x][y][0]=-1LL<<55;
		FOR(j,N) {
			if(count(ALL(S[j]),i)) {
				chmax(dp[A[j]][B[j]][0],E[j]);
				chmax(dp[B[j]][A[j]][0],E[j]);
			}
		}
		FOR(j,29) {
			FOR(x,16) FOR(y,16) dp[x][y][j+1]=-1LL<<55;;
			FOR(x,16) FOR(y,16) FOR(r,16) dp[x][r][j+1]=max(dp[x][r][j+1],dp[x][y][j]+dp[y][r][j]);
		}
		FOR(x,16) ret[i][x]=-1LL<<55;
		for(k=max(0,K[i]-26);k<=K[i];k++) {
			ll cur[16][16];
			FOR(x,16) FOR(y,16) cur[x][y]=(x==y)?0:-1LL<<55;
			FOR(j,29) if(k&(1<<j)) {
				ll to[16][16];
				FOR(x,16) FOR(y,16) to[x][y]=-1LL<<55;
				FOR(x,16) FOR(y,16) FOR(r,16) to[x][r]=max(to[x][r],cur[x][y]+dp[y][r][j]);
				swap(cur,to);
			}
			
			FOR(x,16) ret[i][x]=max(ret[i][x],cur[C[i]][x]);
		}
		for(k=0;k<=min(26,K[i]);k++) {
			ll cur[16][16];
			FOR(x,16) FOR(y,16) cur[x][y]=(x==y)?0:-1LL<<55;
			FOR(j,29) if(k&(1<<j)) {
				ll to[16][16];
				FOR(x,16) FOR(y,16) to[x][y]=-1LL<<55;
				FOR(x,16) FOR(y,16) FOR(r,16) to[x][r]=max(to[x][r],cur[x][y]+dp[y][r][j]);
				swap(cur,to);
			}
			
			FOR(x,16) ret[i][x]=max(ret[i][x],cur[C[i]][x]);
		}
	}
	
	ll ma=-1LL<<55;
	FOR(x,16) {
		ll sum=0;
		FOR(i,26) sum+=ret[i][x];
		ma=max(ma,sum);
	}
	if(ma<-1LL<<54) {
		cout<<"Impossible"<<endl;
	}
	else {
		cout<<ma<<endl;
	}
	
}

まとめ

うーむ。