kmjp's blog

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

AtCoder ARC #007

ARC007はリアルタイム参加できなかったので、後日練習しました。
なんとか4問解けた。
問題はこちら:http://arc007.contest.atcoder.jp/assignments

A 帰ってきた器物損壊!高橋君

1文字と文字列が与えられるので、最初の1文字以外を出力する。
これは特に考えず完了。

char key[10];
char str[100];

void solve() {
	
	ZERO(key);
	ZERO(str);
	GETs(key);
	GETs(str);
	
	char* p = str;
	while(*p) {
		if(*p != key[0]) _P("%c",*p);
		p++;
	}
	_P("\n");
}

B 迷子のCDケース

対象のCD(nu[j])と、今外にあるCD(nc)を交換するという作業を繰り返す。

int N,M;
int nu[101];

void solve() {
	int m,d,i,j;
	int vx,vy,nc;
	
	GET2(&N,&M);
	int c=0;
	FOR(i,N) nu[i]=i+1;
	
	FOR(i,M) {
		nc = GETi();
		if(nc==c) continue;
		FOR(j,N) {
			if(nu[j]==nc) {
				nu[j]=c;
				c=nc;
				break;
			}
		}
	}
	
	FOR(i,N) _P("%d\n",nu[i]);
}

C 節約生活

最大10個しか時間の長さが無いので、各10個の時間それぞれでTVを付け始めたとき、常時TVを見られるか2^10通り列挙した。
「全てのテレビを点けてからは~~」などの前提条件がややこしいので、最初にoが先頭に来るよう文字列をローテートしています。

char str[100];
int bitcount(int n) {
	int i=0;
	while(n>0) {i += n%2; n>>=1;}
	return i;
}

void solve() {
	int m,d,i,w,j;
	int ns=0;
	int s[100];
	int sl;
	int val,goal,min,pat;
	
	GETs(str);
	sl=strlen(str);
	
	while(*str=='x') {
		FOR(i,sl-1) str[i]=str[i+1];
		str[sl-1]='x';
	}
	val=0;
	FOR(i,sl) if(str[i]=='o') val |= 1<<i;
	goal = (1<<sl)-1;
	min = 999;
	FOR(pat,1<<sl) {
		int tv = bitcount(pat);
		if(tv>min) continue;
		
		int t=0;
		FOR(i,sl) {
			if(pat & (1<<i)) t |= (val<<i) | ((val<<i)>>sl);
		}
		
		if((t & goal)==goal) min=tv;
	}
	
	_P("%d\n",min);
}

D 破れた宿題

等差数列の部分文字列から、初項と公差を求める問題。
最初初項と公差を頑張って求めようとしたけど、初項はすぐに求まることが分かった。
というのも、初項は最初の1ケタで確定する。というのも公差を大きく取れば、残りの文字列の数値にできるから。
先頭が0の時だけは注意で、頭に1を付けて、先頭にあるだけの0を付けたのものが初項になる。

残りの文字列の長さを見て、残りが2項目だけか3項目以降も入るか見ている。
3項目以降もありそうな場合、考えられる2項目を全部列挙して、それぞれ3項目以降がちゃんと等差数列になっているか確認している。

実際は、3項目以降もあるパターンは早期に実装できたけど2項目止まりのパターンでコーナーケースが多く正解まで8回もミスってしまった。
今回多倍長演算があるのでPerlで実装。use bigintを行うと整数が勝手に多倍長になるので楽だが、なんか一部小数になるとdoubleの範囲外(10^308以上)になったときオーバーフローして困った。

#!/usr/bin/perl

use Data::Dumper;
use bigint;

while(<>) {
	chomp;
	solve($_);
}

sub solve {
	my $vl = $_[0];
	$fl=1;
	if(substr($vl,0,1)=='0'){
		$f=1;
	}
	else {
		$f = substr($vl,0,1);
		$vl = substr($vl,1);
	}

	while(length($vl)>0 && substr($vl,0,1)=='0') {
		$f*=10;
		$fl++;
		$vl = substr($vl,1);
	}

	$sl=length($vl);
	if($sl==0) {
		out($f,1);
	}
	elsif($sl<$fl) {
		$up=1;
		while(1) {
			$min=$vl*(10**$up);
			$max=($vl+1)*(10**$up)-1;
			if($min>=$f+1) {out($f,$min-$f); last;}
			if($max>=$f+1) {out($f,1); last;}
			$up++;
		}
	}
	elsif($sl==$fl) {
		if($vl<$f+1){ $vl*=10;};
		out($f,$vl-$f);
	}
	else {
		#探す
		
		for $len(1..(length($vl))) {
			$nv = substr($vl,0,$len);
			$dif = 0+$nv-$f;
			if($dif<1){ next;}
			
			$ok=1;
			$left = substr($vl,$len);
			while(length($left)>0) {
				$nv+=$dif;
				if(length($nv)<=length($left)) {
					$t = 0+substr($left,0,length($nv));
					if($t != $nv){ $ok=0; last;}
					$left=substr($left,length($nv));
				}
				else {
					$nvl = 0+substr($nv,0,length($left));
					if($nvl!=$left){ $ok=0;}
					last;
				}
			}
			if($ok==1) {
				out($f,$dif);
				last;
			}
		}
	}
}

sub out {
	printf "%s %s\n",$_[0],$_[1];
}

まとめ

本番出てたらDは部分点取れたかな…?
本番ではないとはいえ、解き切れたのは良かった。
多倍長演算ではいつも迷うので、Pythonを覚えようかな。