メモ

yukicoderでゆるふわgolf

yukicoder No.454 逆2乗和

問題はこちら
No.454 逆2乗和 - yukicoder

実験しながら適当に投げたら通ってしまった

愚直でいけたり?→n=10^9近くまで計算しないとダメっぽい
n=10^7くらいまでで様子見→サンプルに関しては10^-7を足せばちょうどいい感じ
じゃあとりあえずそれを投げてみる→通っちゃった……

ちゃんと考察
適当な項まで足して、残りは誤差項として適当におさえてしまいたい。
誤差項の評価にいい感じの優級数や劣級数は思いつかないのでとりあえず積分で殴ってみる
1/(x+t)^2はtについて単調減少関数であることから
\displaystyle{
\sum_{n=N+1}^{\infty}\frac{1}{(x+n)^{2}}<\int_{N+1}^{\infty}\frac{1}{(x+t-1)^{2}}dt=\frac{1}{x+N}\\
\sum_{n=N+1}^{\infty}\frac{1}{(x+n)^{2}}>\int_{N+1}^{\infty}\frac{1}{(x+t)^{2}}dt=\frac{1}{x+N+1}}
が成立する
(「調和級数 積分」などでググると似たような評価をやってる分かりやすい図が出てくる)
ということで、「第N項まで計算したら、誤差は1/(x+N)くらい」という事がわかる
実際に「第N項までの和に1/(x+N)を足したもの」と真値の差を評価すると
\displaystyle{
0<((\sum_{n=1}^{N}\frac{1}{(x+n)^{2}})+\frac{1}{x+N})-\sum_{n=1}^{\infty}\frac{1}{(x+n)^{2}}\\
<((\sum_{n=1}^{N}\frac{1}{(x+n)^{2}})+\frac{1}{x+N})-((\sum_{n=1}^{N}\frac{1}{(x+n)^{2}})+\frac{1}{x+N+1})\\
=\frac{1}{(x+N)(x+N+1)}}
となるので、例えばN=10^5くらいまで計算すれば要求される精度を得られる

#define N 1000000
int main(){
	double x,s=0;
	int n;
	scanf("%lf",&x);
	for(n=1;n<=N;n++)s+=1/(x+n)/(x+n);
	printf("%.9f",s+1/(x+N));
	return 0;
}

適当に縮める

double s,n;
i;
main(){
	for(scanf("%lf",&n);n++<1e7;)s+=1/n/n;
	i=!printf("%.9f",s+1/n);
}

83B

yukicoder No.117 組み合わせの数

問題はこちら
No.117 組み合わせの数 - yukicoder

素直に階乗で計算するだけ
C(N,K)は\prod_{i=1}^K \frac{N+1-i}{i}による単純なループによりO(K)で計算できるけど、KTが大きいのでこれではTLEになる

\displaystyle{{}_NC_K=\frac{N!}{K!(N-K)!},\ \ {}_NP_K=\frac{N!}{(N-K)!},\ \ {}_NH_K={}_{N+K-1}C_K}
なので、事前に階乗を計算しておけば、各クエリは定数時間で答えられる

P:=10^9+7は素数なので、K<PにおいてK!はPを因数に持たず、互いに素であることから、mod PにおけるK!の逆元は存在する。
逆元はフェルマーの小定理により求める

横着をして、階乗の逆元を毎回求めているのでちょっと遅い

//繰り返し二乗法
long p(long a,int i){return i?p(a*a%P,i/2)*(i%2?a:1)%P:1;}

int main(){
	long frac[2000010];
	int P=1e9+7,n,k,i;
	char c;

	frac[0]=1;
	for(i=1;i<=2000000;i++)frac[i]=frac[i-1]*i%P;
	//H(1000000,1000000)のとき2000000!まで必要(1敗)

	scanf("%d",&i);
	for(;--i;){
		scanf(" %c(%d,%d)",&c,&n,&k)
		if(c=='H')n=n+k>0?n+k-1:0;
		//定義式通りだとH(0,0)のときC(-1,0)になって困るのでちょっと変えた
		if(n<k)puts("0");
		else printf("%d\n",frac[n]*p(frac[n-k]*(c=='P'?1:frac[k])%P,P-2)%P);
	}
	return 0;
}

'C','H','P'がそれぞれ67,72,80なのを利用して識別を工夫
powの引数もいい感じに睨んでやると省略できる事がわかる

long f[1<<21];
P=1e9+7,x,c,k;
p(a,i){return i?1L*p(1L*a*a%P,i/2)*(i%2?a:1)%P:1;}
main(n){
	for(gets(f),*f=1;k++<2e6;f[k]=f[k-1]*k%P);
	for(;x=~scanf(" %c(%d,%d)",&c,&n,&k);printf("%d\n",n<k?0:f[n]*p(f[n-k]*(c%5?f[k]:1)%P,P-2)%P))c&8&&(n+=k)&&n--;
}

240B

2017/03/02追記
pow関数のreturnを省略したい。xが手隙なのでそこへ代入してみる。xをlongにしておくといろいろ省略できる

//関数部分
p(a,i){return i?1L*p(1L*a*a%P,i/2)*(i%2?a:1)%P:1;}
p(a,i){x=i?p(1L*a*a%P,i/2),x*(i%2?a:1)%P:1;}
p(a,i){x=i?p(1L*a*a%P,i/2),i%2?x*a%P:x:1;}

//printf部分
n<k?0:f[n]*p(f[n-k]*(c%5?f[k]:1)%P,P-2)%P
n<k?0:(p(f[n-k]*(c%5?f[k]:1)%P,P-2),f[n]*x%P)
n>=k?p(f[n-k]*(c%5?f[k]:1)%P,P-2),f[n]*x%P:0

ということで-8B+3Bで計5Bの短縮

long f[1<<21],k;
P=1e9+7,x,c;
p(a,i){k=i?p(1L*a*a%P,i/2),i%2?k*a%P:k:1;}
main(n){
	for(gets(f),*f=1;k++<2e6;f[k]=f[k-1]*k%P);
	for(;x=~scanf(" %c(%d,%ld)",&c,&n,&k);printf("%d\n",n>=k?p(f[n-k]*(c%5?f[k]:1)%P,P-2),k*f[n]%P:0))c&8&&(n+=k)&&n--;
}

235B

yukicoder No.453 製薬会社

問題はこちら
No.453 製薬会社 - yukicoder

線形計画法とか知らないけど数学で殴るだけ

製品Aをxkg、製品Bをykgつくるとすると、問題は
3/4*x+2/7*y≦C
1/4*x+5/7*y≦D
x,y≧0
の条件下で1000*x+2000*yを最大化する問題
厳密な証明を書くのが面倒くさくなったので、図でごまかす
下図は3/4*x+2/7*y=C…(1)を青線、1/4*x+5/7*y=D…(2)を赤線としたものであり、C,Dの値によって3パターン用意した。
黄色の領域が上で述べた条件を満たす範囲になる。
f:id:sugarknri:20161205083531p:plain
f:id:sugarknri:20161205084317p:plain
f:id:sugarknri:20161205085406p:plain
緑線が1000x+2000y=sのsを動かしたものであり、黄色部分と共有点を持つような最大のsを求めるのが問題。
この図を眺めると(1)(2)の交点が
・第1象限(軸上含む)ならその点で
・x<0なら(1)上のx=0の点で
・y<0なら(2)上のy=0の点で
それぞれ最大値を取ることが分かる
(厳密な証明は次の2つを示せば良い。「2つの材料をどちらもちょうど使い切る割当てが存在するならそのとき最大」「存在しないならばどちらか一方のみを作ったとき最大」。どちらもそれ以外に最大値があるとして背理法から従う)

(1)(2)の連立方程式を解く
x=4/13*(5C-2D)
y=7/13*(-C+3D)
が得られる。
ということで求めるべき答えは
・5C-2D≧0かつ-C+3D≧0のとき、x,yは上で求めた通りであり1000x+2000y=(3C+17D)*2000/13
・5C-2D<0のとき、x=0,y=7/2*Cであり1000x+2000y=7000C
・-C+3D<0のとき、x=4*D,y=0であり1000x+2000y=4000D

int main(){
	double c,d;
	scanf("%lf%lf",&c,&d);
	if(-c+3*d<0)printf("%f",4000*d);
	else if(5*c-2*d<0)printf("%f",7000*c);
	else printf("%f",(3*c+17*d)*2000/13);
	return 0;
}

ぎゅ

a;main(float b){a=scanf("%d%f",&a,&b)>printf("%f",a>3*b?b*4e3:a<.4*b?a*7e3:(6*a+34*b)/.013);}

93B

perlなら最短狙えるとおもってこれを投げた

<>=~$";print$`<$'*.4?$`*7e3:$'*3<$`?$'*4e3:($`*6+$'*34)/.013

プロに一瞬で抜かれた

<>=~$";print$`<$'*.4?$`*7:$'*3<$`?$'*4:($`*6+$'*34)/13,e3

というかなんだこれは。perl自由すぎるだろ

yukicoder No.429 CupShuffle

問題はこちら
No.429 CupShuffle - yukicoder

上下からシミュレーションするだけ
上から順に??の直前までやり、下から順に??の直前までやれば、その前後で変化している場所が答えだとわかる

#define swap(x,y)(t=x,x=y,y=t)
int main(){
	int a[100010],b[100010],cup1[100010],cup2[100010];
	int i,n,k,x,t;

	//読み込み
	scanf("%d%d%d",&n,&k,&x);
	for(i=1;i<=n;i++)cup1[i]=i;
	for(i=1;i<=k;i++){
		if(i==x)scanf("%*s%*s");
		else scanf("%d%d",a+i,b+i);
	}
	for(i=1;i<=n;i++)scanf("%d",cup2+i);

	//上から
	for(i=1;i<x;i++)swap(cup1[a[i]],cup1[b[i]]);
	//下から
	for(i=k;i>x;i--)swap(cup2[a[i]],cup2[b[i]]);
	//チェック
	for(i=1;i<=n;i++)if(cup1[i]!=cup2[i])printf("%d ",i);
	return 0;
}

当然「下から」シミュレーションをしようとすると変数を保存するための配列が必要になる
適当な再帰関数でごまかすとしても、前半と後半でswapを2回書かなければならなくなるので短くするのは難しそう
どうにか上からできないかと考えたところ、つぎのような物ができた

#define swap(x,y)(t=x,x=y,y=t)
#define min(x,y)(x<y?x:y)
#define max(x,y)(x>y?x:y)
int main(){
	int a,b,cup[100010],pos[100010],end[100010];
	int i,n,k,x,t,ans1,ans2;
	scanf("%d%d%d",&n,&k,&x);
	for(i=1;i<=n;i++)cup[i]=pos[i]=i;
	for(i=1;i<=k;i++){
		if(i==x)scanf("%*s%*s");
		else{
			scanf("%d%d",&a,&b);
			if(i<x)swap(cup[a],cup[b]);
			else swap(pos[a],pos[b]);
		}
	}
	for(i=1;i<=n;i++)scanf("%d",end+i);
	t=0;
	for(i=1;i<=n;i++)if(cup[pos[i]]!=end[i]){
		t++?ans1=pos[i]:(ans2=pos[i]);
	}
	printf("%d %d",min(ans1,ans2),max(ans1,ans2));
	return 0;
}

前半は今まで通り行い、後半は再び{1,2,…,N}という並びからswapを行う。
cup[i]は前半終了時にi番目の位置にいるカップの番号、
pos[i]は最後にi番目の位置にいるカップが、後半開始時に居た位置
を与えるので、??の部分でpos[i]番目の位置にあるカップが入れ替えられていない時かつその時に限り、cup[pos[i]]==end[i]が成立する

endを配列に読み込む必要はないので、この辺をひとまずざっくりとまとめる

i,j,a,p[1<<17],q[1<<17];
main(n,k,x,y,z){
	for(scanf("%d%d%d ",&n,&k,&x);n/++i;)p[i]=q[i]=i;
	for(;k--;)--x?scanf("%d%d ",&y,&z),x>0?p[y]^=p[z]^=p[y]^=p[z]:(q[y]^=q[z]^=q[y]^=q[z]):gets(&y);
	for(;~scanf("%d",&x);x-p[i=q[++j]]?a?z=i:(a=i):0);
	a=!printf("%d %d",a<z?a:z,a<z?z:a);
}

やはりswapを2回書くのは冗長。p,qをまとめてp[2][1<<17]にして短縮

i,j,a,p[2][1<<17],*q=p;
main(n,k,x,y,z){
	for(scanf("%d%d%d ",&n,&k,&x);n/++i;)q[i]=p[1][i]=i;
	for(;k--;)--x?scanf("%d%d ",&y,&z),i=x<0,p[i][y]^=p[i][z]^=p[i][y]^=p[i][z]:gets(&y);
	for(;~scanf("%d",&x);x-q[i=p[1][++j]]?a?z=i:(a=i):0);
	a=!printf("%d %d",a<z?a:z,a<z?z:a);
}

とりあえずぱぱっとfor文を圧縮してみよう
単純に圧縮すると毎回n/++iが評価されるようになるから、2つ目のループ内でiを一時変数に使うことはできない
手隙の変数がないかと睨んだら、とりあえずnが使える

for(scanf("%d%d%d ",&n,&k,&x);n/++i;)q[i]=p[1][i]=i;for(;k--;)--x?scanf("%d%d ",&y,&z),i=x<0,p[i][y]^=p[i][z]^=p[i][y]^=p[i][z]:gets(&y);
for(scanf("%d%d%d ",&n,&k,&x);n/++i?q[i]=p[1][i]=i:k--?--x?scanf("%d%d ",&y,&z),n=x<0,p[n][y]^=p[n][z]^=p[n][y]^=p[n][z]:gets(&y):0;);

最後のも合体。一時変数はiのままでもよいが、他に影響のないyに取り替えておく

for(scanf("%d%d%d ",&n,&k,&x);n/++i?q[i]=p[1][i]=i:k--?--x?scanf("%d%d ",&y,&z),n=x<0,p[n][y]^=p[n][z]^=p[n][y]^=p[n][z]:gets(&y):0;);for(;~scanf("%d",&x);x-q[i=p[1][++j]]?a?z=i:(a=i):0);
for(scanf("%d%d%d ",&n,&k,&x);n/++i?q[i]=p[1][i]=i:k?--k,--x?scanf("%d%d ",&y,&z),n=x<0,p[n][y]^=p[n][z]^=p[n][y]^=p[n][z]:gets(&y):~scanf("%d",&x)?x-q[y=p[1][++j]]?a?z=y:(a=y):1:0;);

変数を減らしたい
一番多いのは2つめのループの部分で、ループ変数としてk,x、入力の読み込みにy,z、一時変数にnを使っている
3つめのループ変数と答えの保存用に、0になっている変数が2つ欲しい。x,y,zはそうできず、kは使いまわせない。
ぐっと睨むとループ変数にiが使えることが分かるが、答えの保存用にnを使うことはできないのでこれら以外の変数が必要
結局、消せるのはjだけ

for(scanf("%d%d%d ",&n,&k,&x);n/++i?q[i]=p[1][i]=i:k?--k,--x?scanf("%d%d ",&y,&z),n=x<0,p[n][y]^=p[n][z]^=p[n][y]^=p[n][z]:gets(&y):~scanf("%d",&x)?x-q[y=p[1][++j]]?a?z=y:(a=y):1:0;);
for(scanf("%d%d%d ",&n,&k,&x);n/++i?q[i]=p[1][i]=i:k?--k,i=n=--x>0,x?scanf("%d%d ",&y,&z),p[n][y]^=p[n][z]^=p[n][y]^=p[n][z]:gets(&y):~scanf("%d",&x)?x-p[1][y=q[i]]?a?z=y:(a=y):1:0;);
//2つめのループ終了時にiが0となるよう、p[0]とp[1]を入れ替えた
//同じ長さだが変数の宣言部分で2B得

全体では以下のようになる

i,a,p[2][1<<17],*q=p;
main(n,k,x,y,z){
	for(scanf("%d%d%d ",&n,&k,&x);n/++i?q[i]=p[1][i]=i:
				      k?--k,i=n=--x>0,x?scanf("%d%d ",&y,&z),p[n][y]^=p[n][z]^=p[n][y]^=p[n][z]:gets(&y):
				      ~scanf("%d",&x)?x-p[1][y=q[i]]?a?z=y:(a=y):1:
				      0;);
	a=!printf("%d %d",a<z?a:z,a<z?z:a);
}

256B

yukicoder No.446 ゆきこーだーの雨と雪 (1)

問題はこちら
No.446 ゆきこーだーの雨と雪 (1) - yukicoder

0~12345のいずれかと文字列として一致しているかどうかを調べるだけ
c++ならto_string関数なるものがあるらしいが、cにはない
文字列を数値にするならatoiという関数があるのでitoaとかないのかと思いぐぐったところ、非標準ライブラリらしく使えない
代わりにsprintfを使えとwikipediaには書いてあった

int f(char*s){
	char t[10];
	int i;
	for(i=0;i<=12345;i++){
		sprintf(t,"%d",i);
		if(strcmp(s,t)==0)return 1;
	}
	return 0;
}

int main(){
	char a[10],b[10];
	gets(a);
	gets(b);
	puts(f(a)&&f(b)?"OK":"NG");
	return 0;
}

Aに対する処理とBに対する処理が全く同じなので適当に縮めて

f;char s[9],t[9];
main(n){
	for(;gets(s);)for(n=12346;n;f+=!strcmp(s,t))sprintf(t,"%d",--n);
	n=!puts(f-2?"NG":"OK");
}

入力は高々5文字('\0'含め6文字)だから、char型の配列を作らなくてもint型の変数に押し込めそう。
あとはいい感じにループ圧縮をして

n,s;main(f,t){for(;n?sprintf(&t,"%d",--n),f-=!strcmp(&s,&t):(n=gets(&s)?12346:!puts(~f?"NG":"OK")););}

102B

そういえばstrtolとかいう便利関数があったのでこっちの可能性も探る

f;
char*t,s[];
main(){
	for(;gets(s);)f|=strtol(s,&t,0)>12345|*t|(*s==48&&s[1]);
	f=!puts(f?"NG":"OK");
}

よく考えると*s==48は*s<49と書き換えられる。
ここで、&は|よりも優先度が高いことから、次のように縮められる

strtol(s,&t,0)>12345|*t|(*s==48&&s[1])
strtol(s,&t,0)>12345|*t|(*s<49&&s[1])
strtol(s,&t,0)>12345|*t|*s<49&!!s[1]

ということで

f;
char*t,s[];
main(){
	for(;gets(s);)f|=strtol(s,&t,0)>12345|*t|*s<49&!!s[1];
	f=!puts(f?"NG":"OK");
}

96B