kmjp's blog

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

TopCoder SRM 816 : Div1 Medium OccupiedAirplane

なんだこれ…。
https://community.topcoder.com/stat?c=problem_statement&pm=17222&rd=18894

問題

6列・R行からなる飛行機の座席群がある。
各座席の番号は、row-major orderで0-indexで振られている。

各列両端の座席は窓側、3列目・4列目の座席は通路側、2列目・5列目の座席は中央側と呼ぶことにする。
今、飛行機にいくつかの団体が順に搭乗してくる。
各団体は以下のように座席につく。

  • 残座席が団体の人数以下の場合、そこで終了。
  • 団体が1名の場合、窓側→通路側→中央側の順で、空き席があれば番号が最小のところに座る。
  • 団体が2名以上の場合、全員が同じ行に座れるなら、そのうち左から詰めて座る。
    • 全員が同じ行に座れないなら、各自1名の団体が複数あるかのように座る。

団体の人数が与えられたとき、各人の座席を答えよ。

解法

1名の団体向けに、各列における空き行のsetを保持しつつ、2名以上の団体向けに、n席以上の空き席がある行のsetを管理しよう。

int avail[101010];
set<int> S[7];
set<int> C[7];

class OccupiedAirplane {
	public:
	long long seat(int R, vector <int> groups, int seed) {
		int i,j;
		FOR(i,7) S[i].clear(),C[i].clear();
		int lef=R*6;
		FOR(i,R) {
			avail[i]=(1<<6)-1;
			for(j=1;j<=6;j++) S[j].insert(i);
			FOR(j,3) C[j].insert(i);
		}
		ll state=seed;
		FOR(i,600001) {
			 state = (state * 1103515245 + 12345)%(1LL<<31);
			 int g = ((state >> 10)%6) + 1;
			 groups.push_back(g);
		}
			 
		ll num=1;
		ll ret=0;
		FORR(g,groups) {
			vector<int> G;
			if(g>lef) break;
			lef-=g;
			if(S[g].size()) {
				G.push_back(g);
			}
			else {
				while(g--) G.push_back(1);
			}
			FORR(a,G) {
				int row;
				if(a==1) {
					vector<int> O={0,2,1};
					FORR(i,O) if(C[i].size()) {
						row=*C[i].begin();
						j=i;
						if((avail[row]&(1<<j))==0) j=5-j;
						avail[row]^=1<<j;
						ret+=num*(row*6+j);
						num++;
						break;
					}
				}
				else {
					row=*S[a].begin();
					FOR(i,6) if(a&&(avail[row]&(1<<i))) {
						a--;
						avail[row]^=1<<i;
						ret+=num*(row*6+i);
						num++;
					}
				}
				
				FOR(j,7) S[j].erase(row),C[j].erase(row);
				for(j=1;j<=6;j++) if(__builtin_popcount(avail[row])>=j) S[j].insert(row);
				FOR(j,6) if(avail[row]&(1<<j)) C[min(j,5-j)].insert(row);
			}
		}
		return ret;
		
	}
}

まとめ

面倒なだけで面白みを感じない問題で、かつ凡ミスで落としたのしょうもなさ過ぎてしんどい。