kmjp's blog

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

yukicoder : No.1369 交換門松列・竹

今年も恒例の門松列。
https://yukicoder.me/problems/no/1369

問題

門松列列ではない数列が与えられる。
2要素を選び1度だけswapし、門松列列にできるか判定せよ。

解法

1個要素をいじると、両隣を含め最大3か所で門松列かどうかが変化する。
ということは、2要素のswapでは高々6か所までしか変化しない。

初期状態で門松列でない箇所が7か所以上ある場合解なし。
6か所以下の場合、少なくともswapの片方はその6か所のいずれかなので、それぞれもう片方を総当たりし、門松列に違反するものがないか判定しよう。

int T,N;
int A[505050];

int iskado(int cur) {
	if(cur<0||cur>=N) return 0;
	if(cur==0||cur==N-1) return 1;
	int L=cur-1;
	int R=cur+1;
	if(A[cur]==A[L]) return 0;
	if(A[cur]==A[R]) return 0;
	if(A[L]==A[R]) return 0;
	return (A[L]<A[cur])==(A[R]<A[cur]);
}

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>T;
	while(T--) {
		cin>>N;
		FOR(i,N) cin>>A[i];
		vector<int> cand;
		FOR(i,N) if(iskado(i)==0) cand.push_back(i);
		
		if(cand.size()>6) {
			cout<<"No"<<endl;
			continue;
		}
		int ret=0;
		FORR(x,cand) {
			for(int c=max(0,x-1);c<=min(x+1,N-1);c++) {
				FOR(i,N) if(i!=c) {
					int cur=N-cand.size();
					set<int> S;
					S.insert(i-1);
					S.insert(i);
					S.insert(i+1);
					S.insert(c-1);
					S.insert(c);
					S.insert(c+1);
					FORR(s,S) cur-=iskado(s);
					swap(A[i],A[c]);
					FORR(s,S) cur+=iskado(s);
					if(cur==N) ret=1;
					swap(A[i],A[c]);
				}
			}
		}
		if(ret) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
		
	}
}

まとめ

よくこれだけ毎年問題考えつくよね。