メモ

yukicoderでゆるふわgolf

yukicoder No.509 塗りつぶしツール

問題はこち
No.509 塗りつぶしツール - yukicoder

また、白でも黒でもない3つ目の色を赤と呼ぶことにする。
領域を「背景」「文字」「穴」の3つの領域に呼び分ける。
背景を塗りつぶすのは1回、文字をすべて塗りつぶすには文字数回、穴をすべて塗りつぶすには穴の個数回の操作が必要。

・背景を1回だけ塗りつぶす場合
背景の色は「白→黒」と変更されるので、そのとき文字の色は赤になっていなければならない。
すなわち各文字の色は2回以上変更されるので、全体では最低でも2*(文字数)+(穴数+1)回の操作が必要。
実際に「文字をすべて赤にする→背景と穴をすべて黒にする→文字をすべて白にする」という手順で最小回数を達成できる
・文字を1回だけ塗りつぶす場合
文字の色は「黒→白」と変更されるので、そのとき背景及び穴の色は赤になっていなければならない。
すなわち背景及び穴の色は2回以上変更されるので、全体では最低でも2*(穴数+1)+文字数回の操作が必要。
実際に「背景と穴をすべて赤にする→文字をすべて白にする→背景と穴をすべて黒にする」という手順で最小回数を達成できる
・背景,文字をどちらも2回以上塗りつぶす場合
最低でも2*(文字数)+(穴数)+2回の変更が必要なのでこれは1番目のパターンより必ず回数が多くなる

ということで、結局min{2*(文字数)+(穴数+1),2*(穴数+1)+文字数}が答えになることがわかる。

#define min(p,q)(p<q?p:q)
a[]={1,0,0,0,1,0,1,0,2,1};
s,h,x;
main(){
	while(1){
		x=getchar();
		if(x=='\n')break;
		s++;
		h+=a[x-'0'];
	}
	printf("%d",min(2*(h+1)+s,2*s+h+1));
}

縮める際の肝は上のコードで配列に保存していた部分をどうするか。
方針としては2通りあって、「いい感じの式をつくる」「bitでやる」
文字の入力の受取も、getchar()-10、~getchar()、read()、などいろいろ考えられるのでいい感じになるものを探す

s,x;main(h){for(;x=getchar()-10;s++)h+=3&397569>>x*2-12;printf("%d",s+h+(s<h?s:h));}
s,x;main(h){for(;x=getchar()-10;s++)h+=x/46-~x%2-!(x%8);printf("%d",s+h+(s<h?s:h));}
h,x;main(s){for(;x=~getchar();s--)h+=3&1446145>>~x*2;printf("%d",h-s+(-s<h?-s:h));}
h,x;main(s){for(;x=~getchar();s--)h-=x/57+x%2+!(x%17);printf("%d",h-s+(-s<h?-s:h));}
h,x;main(s){for(;read(0,&x,1);s--)h+=3&1446145>>x*2;printf("%d",h-s+(-s<h?-s:h));}
h,x;main(s){for(;read(0,&x,1);s--)h+=x/56-~x%2-!(x%25);printf("%d",h-s+(-s<h?-s:h));}
s,x;main(h){for(;read(0,&x,1);s++)h+=x/56-~x%2-!(x%10);printf("%d",h+--s+(s<h?s:h));}

int型に対するbitshiftはmod32で計算される仕様と'0'*2 mod 32が0であることがうまく噛み合って、bitの方が短くなった。
82B

yukicoder No.508 超ゆとり教育

問題はこち
No.508 超ゆとり教育 - yukicoder
正確な値は√(N/π)だが、√(N/π)≦√N≦10^6なので、誤差制約から1~10^6の何を出力しても正解になることがわかる。
むしろ真面目に√(N/π)を計算してしまうとN≦3のときに0となってWAの原因になる。

main(){puts("1");}

18B
そういえばreturn 0;をしなくて良くなったので、

a;main(){a=!puts("1");}

とする必要があったときに比べて5B短くて済むようになったのだなあ

yukicoder No.507 ゲーム大会(チーム決め)

問題はこち
No.507 ゲーム大会(チーム決め) - yukicoder

K君以外の人のスコアを昇順にソートしておく
何点の人と組めばよいかを二分探索で求める。
X点の組がM位タイ以内に入れるというのは、X点より真に大きな点の組がM組未満であることと同値
これは尺取法によりO(N)で求まる。
二分探索が収束するまでO(logN)回やるので全体としてはO(NlogN)

a[1<<17];n,M,l,r,m,ll,rr,k;
c(int*p,int*q){return*p-*q;}
main(){
	scanf("%d%d",&n,&M);
	for(int i=0;i<n;i++)scanf("%d",a+i);
	qsort(a+1,n-1,4,c);
	l=0;r=n;
	while(r-l>1){
		//半開区間(l,r]での二分探索
		//(rは解なしのときと対応)
		m=(l+r)/2;
		ll=1;rr=n-1;k=0;
		while(ll<rr){
			//尺取法
			//得点が高い人は、できるだけ低い点の人と組む
			if(ll==m)ll++;
			else if(rr==m)rr--;
			else if(a[ll]+a[rr]>a[0]+a[m])k++,ll++,rr--;
			else ll++;
		}
		if(k<M)r=m;
		else l=m;
	}
	printf("%d",r==n?-1:a[r]);
}


まずはこの部分を縮める

if(ll==m)ll++;
else if(rr==m)rr--;
else if(a[ll]+a[rr]>a[0]+a[m])k++,ll++,rr--;
else ll++;

rr==m?rr--:
ll==m?ll--:
a[ll]+a[rr]>a[0]+a[m]?k++,ll++,rr--:ll++;

rr==m?rr--:
(ll!=m&&a[ll]+a[rr]>a[0]+a[m]?k++,rr--:0,ll++);

rr-m?ll-m&&a[ll]+a[rr]>a[0]+a[m]?k++,rr--:0,ll++:rr--;

読み込みを全部aにまとめる。indexのズレに気をつけながら縮めると

a[1<<17],p,q,l,r,m;
c(int*p,int*q){m=*p-*q;}
main(k){
	for(l=2;~scanf("%d",a+r);r++);
	for(qsort(a+3,r-3,4,c);r-l>1;k<a[1]?r=m:(l=m))
		for(m=l+r>>1,p=3,q=*a+1,k=0;p<q;q-m?p-m&&a[p]+a[q]>a[2]+a[m]?q--,k++:0,p++:q--);
	printf("%d",a[r]?:-1);
}

ぐっと睨んで、一番短くなるようにindexをずらす

a[1<<17],p,q,l,r,m;c(int*p,int*q){m=*p-*q;}main(k){for(l=2;~scanf("%d",a+r);r++);for(qsort(a+3,r-3,4,c);r-l>1;k<a[1]?r=m:(l=m))for(m=l+r>>1,p=3,q=*a+1,k=0;p<q;q-m?p-m&&a[p]+a[q]>a[2]+a[m]?q--,k++:0,p++:q--);printf("%d",a[r]?:-1);}
a[1<<17],p,q,k,r,m;c(int*p,int*q){m=*p-*q;}main(l){for(;~scanf("%d",a-1+r);r++);for(qsort(a+2,r-3,4,c);r-l>1;k<*a?r=m:(l=m))for(m=l+r>>1,p=2,q=a[-1],k=0;p<q;q-m?p-m&&a[p]+a[q]>a[1]+a[m]?q--,k++:0,p++:q--);printf("%d",a[r]?:-1);}
a[1<<17],p,q,l,r,m;c(int*p,int*q){m=*p-*q;}main(k){for(;~scanf("%d",a-2+r);r++);for(qsort(a+1,r-3,4,c);r-l>1;k<a[-1]?r=m:(l=m))for(m=l+r>>1,p=1,q=a[-2],k=0;p<q;q-m?p-m&&a[p]+a[q]>*a+a[m]?q--,k++:0,p++:q--);printf("%d",a[r]?:-1);}
a[1<<17],p,q,k,r,m;c(int*p,int*q){m=*p-*q;}main(l){for(;~scanf("%d",a-3+r);r++);for(qsort(a,r-3,4,c);r+l>1;k<a[-2]?r=m:(l=-m))for(m=r-l>>1,q=a[-3],p=k=0;p<q;q-m?p-m&&a[p]+a[q]>a[-1]+a[m]?q--,k++:0,p++:q--);printf("%d",a[r]?:-1);}

ということで結局一番短くなるのは上から2番目の

a[1<<17],p,q,k,r,m;
c(int*p,int*q){m=*p-*q;}
main(l){
	for(;~scanf("%d",a-1+r);r++);
	for(qsort(a+2,r-3,4,c);r-l>1;k<*a?r=m:(l=m))
		for(m=l+r>>1,p=2,q=a[-1],k=0;p<q;q-m?p-m&&a[p]+a[q]>a[1]+a[m]?q--,k++:0,p++:q--);
	printf("%d",a[r]?:-1);
}

228B

yukicoder No.504 ゲーム大会(ランキング)

問題はこちら
No.504 ゲーム大会(ランキング) - yukicoder

最初は1位で、自分より真に大きなスコアがでたら順位が1つ下がる。

n,s,t,rank;
main(){
	scanf("%d%d",&n,&s);
	rank=1;
	puts("1");
	for(int i=1;i<n;i++){
		scanf("%d",&t);
		if(t>s)rank++;
		printf("%d\n",rank);
	}
}

1番目,2番目の値に対してのみ特殊な処理が必要なので、その辺をmain第一引数を使っていい感じにする

n,s,t;main(i){for(;~scanf("%d",&n);i&&printf("%d\n",s))i--?s+=n>t:(t=n);}

73B

2017/08/01追記
よく考えたらまとめられる
1番目の値を読み込んだときに1を出力し、2番目の値を読み込んだときに何も出力しない

n,s,t;main(i){for(;~scanf("%d",&n);)i--?printf("%d\n",s+=n>t):(t=n);}

69B

No.501 穴と文字列 - yukicoder

問題はこちら
No.501 穴と文字列 - yukicoder

できるだけ多くAを使えば良い
また穴が2個,0個の文字が必要なときは、それぞれB,Cを使えば良い(辞書順最小なので)。
・D≧Nのとき
Aだけでは穴が足りないので末尾にBを増やす必要がある。
Aを1つBに取り替える毎に穴は1つ増えるので、Aを2*N-D個、BをD-N個にすればよい
・D≦Nのとき
Aだけでは穴が多いので末尾にCを増やす必要がある。
Aを1つCに取り替える毎に穴は1つ減るので、AをD個、CをN-D個にすればよい

i,n,d;
main(){
	scanf("%d%d",&n,&d);
	if(d>n){
		for(i=0;i<n+n-d;i++)putchar('A');
		for(;i<n;i++)putchar('B');
	}else{
		for(i=0;i<d;i++)putchar('A');
		for(;i<n;i++)putchar('C');
	}
}

ぐっと睨むと、Aの個数がmin(D,2*N-D)であることがわかるので、分岐をまとめることができ

#define min(p,q)(p<q?p:q)
i,n,d;
main(){
	scanf("%d%d",&n,&d);
	for(i=0;i<min(d,2*n-d);i++)putchar('A');
	for(;i<n;i++)putchar(d>n?'B':'C');
}

さらに2つのfor文もまとめて

#define min(p,q)(p<q?p:q)
i,n,d;
main(){
	scanf("%d%d",&n,&d);
	for(i=0;i<n;i++)putchar(i<min(d,2*n-d)?'A':d>n?'B':'C');
}

分岐を逆にするなど適当に縮めて

i;main(n,c){for(scanf("%d%d",&n,&c);i++<n;putchar(i>c|i>n+n-c?66|c<n:65));}

75B