メモ

yukicoderでゆるふわgolf

yukicoder No.243 出席番号(2)

問題はこちら
No.243 出席番号(2) - yukicoder

出席番号を1ずらして1-based index、すなわち\{1,\ldots,N\}とする。
X=\{1,\ldots,N\}とおく。
\mathcal{P}(X)Xのべき集合、すなわち\mathcal{P}(X)=\{S | S\subset X\}とする。
f:X→\mathbb{N}
f(i)=(iが嫌いな人の数)
とする。
F:\mathcal{P}(X)→\mathbb{N}
F(S)=(「i \in S \Rightarrow iが嫌いな人に出席番号iが割り当てられている」を満たす割り振りの個数)
とする。
定義より、F(S)=(N-|S|)!*\prod_{x\in S}f(x)である。
(Sの元であるようなxは、それが嫌いな誰かに割り当てられているのでf(x)通り。Sの元を全て割り当てた残りの(N-|S|)個は好きなように割り当てられるので(N-|S|)!通り)

包除原理より、求める答えは\sum_{S\in \mathcal{P}(X)}(-1)^{|S|}*F(S)である。
これを変形していく。
\displaystyle {\sum_{S\in \mathcal{P}(X)}(-1)^{|S|}*F(S)\\
=\sum_{S\in \mathcal{P}(X)}(-1)^{|S|}*(N-|S|)!*\prod_{x\in S}f(x)\\
=\sum_{i=0}^{N}\sum_{|S|=i}(-1)^{|S|}*(N-|S|)!*\prod_{x\in S}f(x)\\
=\sum_{i=0}^{N}(-1)^{i}*(N-i)!*\sum_{|S|=i}\prod_{x\in S}f(x)}
ここでdp[k][i]=\displaystyle{\sum_{|S|=i \atop S\subset\{1,\ldots,k\}}\prod_{x\in S}f(x)}とおく。
(ただしk=0のとき\{1,\ldots,k\}空集合を表すとする)

\displaystyle{
dp[k][i]\\
=\sum_{|S|=i \atop S\subset\{1,\ldots,k\}}\prod_{x\in S}f(x)\\
=\sum_{|S|=i \atop S\subset\{1,\ldots,k-1\}}\prod_{x\in S}f(x)+\sum_{|T|=i-1 \atop T\subset\{1,\ldots,k-1\}}f(k)*\prod_{x\in T}f(x)\\
=dp[k-1][i]+dp[k-1][i-1]*f(k)
}
(2行目から3行目への変形は、Sに関する和を、kを含むものと含まないものに分解することにより得られる)
dp[0][0]=1を初期値とするdpにより、dp[N][*]はO(N^2)で求めることができる。
求める答えは
\sum_{i=0}^{N}(-1)^i*(N-i)!*dp[N][i]
だった。

d[k][*]はd[k-1][*]のみから決まるので、更新順序に気を付けて1次元にすることができる

P=1e9+7;
f[6000],n,t,ans;
long dp[6000],fact[6000];
main(){
	fact[0]=1;
	for(int i=1;i<5010;i++)fact[i]=fact[i-1]*i%P;
	
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",&t);
		f[t]++;
	}
	
	dp[0]=1;
	for(int k=0;k<n;k++)for(int i=k;i>=0;i--)dp[i+1]=(dp[i+1]+dp[i]*f[k])%P;
	for(int i=0;i<=n;i++)ans=(ans+(i%2?-1:1)*fact[n-i]*dp[i])%P;
	printf("%d",(ans+P)%P);
}

ゴルフは後で

yukicoder No.565 回転拡大

問題はこちら
No.565 回転拡大 - yukicoder

90度回転する操作により(i,j)にうつるようなマスは(h-1-j,i)である。(0-based index)
よって
new[i][j]=old[h-1-j][i];
という感じのことをi,jについてループでやればよい。
180度、270度回転はこの操作を繰り返し用いれば良い。

r,k,h,w,i,j,n;
char s[5][11][11];
main(){
	scanf("%d%d%d%d\n",&r,&k,&h,&w);
	for(i=0;i<h;i++)gets(s[0][i]);
	for(n=0;n*90<r;n++){
		for(i=0;i<w;i++)for(j=0;j<h;j++)s[n+1][i][j]=s[n][h-1-j][i];
		h^=w^=h^=w;//縦横のサイズを入れ替えるのを忘れないように
	}
	for(i=0;i<h*k;i++){
		for(j=0;j<w*k;j++)putchar(s[n][i/k][j/k]);
		puts("");
	}
}

これを縮める。入力を読み込む先もs[0]ではなくs[r/90]とし、上のコードとは逆順に使ってs[0]を出力することにする。これにより回転数のカウンタnが不要になる
とりあえずパッと見でこう

i,j;
char s[5][11][11];
main(r,k,h,w){
	for(scanf("%d%d%d%d\n",&r,&k,&h,&w),r/=90;gets(s[r][i++]););
	for(;r--;h^=w^=h^=w)for(i=w;i--;)for(j=h;j--;)s[r][i][j]=s[r+1][h-1-j][i];
	for(i=0;i<h*k;i++,puts(""))for(j=0;j<w*k;)putchar(s[n][i/k][j++/k]);
}

まず2つめのfor文に注目する。
最初にここにきたとき、iの値はh+1。iとjを入れ替えることにより、hとwのswapとiの初期化が噛み合って1B縮む

for(;r--;h^=w^=h^=w)for(i=h;i--;)for(j=w;j--;)s[r][j][i]=s[r+1][h-1-i][j];
for(;r--;i=h^=w^=h^=w)for(;i--;)for(j=w;j--;)s[r][j][i]=s[r+1][h-1-i][j];

次に3つ目のfor文を見る。最初にi=0としているが、これは新しい変数を使えば1B短縮することが出来る
しかし、ここに来た時にはrが-1になっているのでこれが使えるので、そのほうがもっと縮む

for(i=0;i<h*k;i++,puts(""))for(j=0;j<w*k;)putchar(s[n][i/k][j++/k]);
for(;++r<h*k;puts(""))for(j=0;j<w*k;)putchar(s[n][r/k][j++/k]);

これを圧縮してみる

for(;++r<h*k;puts(""))for(j=0;j<w*k;)putchar(s[n][r/k][j++/k]);
//形式的に書き換えただけ↓
for(;j<w*k?putchar(s[n][r/k][j++/k]):(j=!puts(""),++r<h*k););
for(;j/w/k?j=!puts(""),++r<h*k:putchar(s[n][r/k][j++/k]););//-4B
//正しくはこう↓
//for(j=0;j/w/k?j=!puts(""),++r+1<h*k:putchar(s[n][(r+1)/k][j++/k]););

最初に来た時、jは回転0度なら0、そうでなければ-1と、一定の値になっていないので困る。
さらにrもincが後になったので1ずれて困る
新しい変数を用意するとそれぞれ2Bかかるが、2つ目のfor文を調整することで+1Bうまくいくので差し引き3B短縮
ということでこれらをまとめるとこう

i,j;
char s[5][11][11];
main(r,k,h,w){
	for(scanf("%d%d%d%d\n",&r,&k,&h,&w),r/=90;gets(s[r][i++]););
	for(;r;i=h^=w^=h^=w)for(r--;i--;)for(j=w;j;s[r][j][i]=s[r+1][h-1-i][--j]);
	for(;j/w/k?j=!puts(""),++r<h*k:putchar(s[0][r/k][j++/k]););
}

230B

yukicoder No.326 あみだますたー

問題はこちら
No.326 あみだますたー - yukicoder

{1,…,N}上の順序 "<" を i < j ⇔ Ai < Aj により定める
このとき、「あみだくじによって数列{Ai}を作ること」と「順序"<"によってバブルソートをすること」が対応する
(完成するあみだくじにおいて左にある数ほど"小さい"と考えるということ)

ということでバブルソートを実装するだけ

d[110],g[110],ans[6000];c,i,j,n,k,t,a,b;
main(){
	scanf("%d%d",&n,&k);
	for(i=1;i<=n;i++)d[i]=i;
	while(k--){
		scanf("%d%d",&a,&b);
		d[a]^=d[b]^=d[a]^=d[b];
	}
	for(i=1;i<=n;i++)scanf("%d",g+i);
	for(i=1;i<=n;i++)for(j=1;j<=n-i;j++)if(g[d[j]]>g[d[j+1]]){//ここのif文の条件が普通のバブルソートと違う
		ans[c++]=j;
		d[j]^=d[j+1]^=d[j]^=d[j+1];
	}
	printf("%d\n",c);
	for(i=0;i<c;i++)printf("%d %d\n",ans[i],ans[i]+1);
}

さてこれを縮めよう
この問題はスペシャルジャッジなので、出力を全て空白区切りで行っても大丈夫であることを利用してprintfを1つにまとめることができる
また大量にある変数たちもぐっと睨むことでいくつか減らすことが出来る
とりあえずこう

d[110],g[110],s[6000];c,i,j;
main(n,k,a,b){
	for(scanf("%d%d",&n,&k);i++<n;)d[i]=i;
	for(;k--;d[a]^=d[b]^=d[a]^=d[b])scanf("%d%d",&a,&b);
	for(;~scanf("%d",++j+g););
	for(;n--;)for(i=0;i++<n;)g[d[i]]>g[d[i+1]]?d[s[c++]=i]^=d[i+1]^=d[i]^=d[i+1]:0;
	for(;k<c*2;k++)printf("%d ",~k?s[k/2]+k%2:c);//kは-1から
}

for文をまとめていく
前の方に出てくる変数を後ろの方でうっかりいじらないように注意する
また、3つ目のfor文に出てくるjのincを上手く使って、それ以降のループを調整する

	//1つ目+2つ目
	for(scanf("%d%d",&n,&k);i++<n;)d[i]=i;for(;k--;d[a]^=d[b]^=d[a]^=d[b])scanf("%d%d",&a,&b);
	for(scanf("%d%d",&n,&k);i++<n?d[i]=i:k--?scanf("%d%d",&a,&b),d[a]^=d[b]^=d[a]^=d[b]:0;);
	for(;i++<105?d[i]=i:k--?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a):0;);
	//scanfを1つにできた(ただし初期値としてk≠0,n=0が必要)

	//+3つ目
	for(;i++<105?d[i]=i:k--?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a):0;);for(;~scanf("%d",++j+g););
	for(;i++<105?d[i]=i:k-->0?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a):~scanf("%d",++j+g););

	//4つ目のネストを1重に
	for(;n--;)for(i=0;i++<n;)g[d[i]]>g[d[i+1]]?d[s[c++]=i]^=d[i+1]^=d[i]^=d[i+1]:0;
	for(;j++<n?g[d[j]]>g[d[j+1]]?d[s[c++]=j]^=d[j+1]^=d[j]^=d[j+1]:1:(j=0,n--););

	//+4つ目
	for(;i++<105?d[i]=i:k-->0?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a):~scanf("%d",++j+g););for(;j++<n?g[d[j]]>g[d[j+1]]?d[s[c++]=j]^=d[j+1]^=d[j]^=d[j+1]:1:(j=0,n--););
	for(;i++<105?d[i]=i:k-->0?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a):~scanf("%d",++j+g)?:n/j?g[d[j]]>g[d[j+1]]?d[s[c++]=j]^=d[j+1]^=d[j]^=d[j+1]:1:(j=0,n--););

	//+5つ目
	for(;i++<105?d[i]=i:k-->0?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a):~scanf("%d",++j+g)?:n/j?g[d[j]]>g[d[j+1]]?d[s[c++]=j]^=d[j+1]^=d[j]^=d[j+1]:1:(j=0,n--););for(;k<c*2;k++)printf("%d ",~k?s[k/2]+k%2:c);//kは-1から
	for(;i++<105?d[i]=i:k-->0?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a):~scanf("%d",++j+g)?:n/j?g[d[j]]>g[d[j+1]]?d[s[c++]=j]^=d[j+1]^=d[j]^=d[j+1]:1:n?j=!n--,1:printf("%d ",j-1?s[j/2]+j%2:c)&&j-2*c-1;);//jは1から
	for(;i++<105?d[i]=i:k-->0?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a):~scanf("%d",++j+g)?:n/j?g[d[j]]>g[d[j+1]]?d[s[c++]=j]^=d[j+1]^=d[j]^=d[j+1]:1:n?j=!n--,1:j-2*c-!!printf("%d ",j-1?s[j/2]+j%2:c););

というわけで完成したものがこれ

d[110],g[110],s[6000];c,i,j,n;
main(k,a,b){
	for(;i++<105?d[i]=i://1つ目
		k-->0?scanf("%d%d",&a,&b),n?d[a]^=d[b]^=d[a]^=d[b]:(k=b,n=a)://2つ目
			~scanf("%d",++j+g)?://3つ目
				n/j?g[d[j]]>g[d[j+1]]?d[s[++c]=j]^=d[j+1]^=d[j]^=d[j+1]:1://4つ目の内側
					n?j=!n--,1://4つ目の外側
						j-2*c-!!printf("%d ",j-1?s[j/2]+j%2:c);//5つ目
	);
}

254B

yukicoder No.556 仁義なきサルたち

問題はこちら
No.556 仁義なきサルたち - yukicoder

Union-Findするだけ

int uniroot[10010],unicnt[10010];
void ufinit(int n){for(int i=0;i<n;i++){uniroot[i]=i;unicnt[i]=1;}}
int root(int x){return uniroot[x]!=x?uniroot[x]=root(uniroot[x]):x;}
void uni(int x,int y){if((x=root(x))==(y=root(y)))return;uniroot[x]=y;unicnt[y]+=unicnt[x];}

n,m,a,b;
main(){
	scanf("%d%d",&n,&m);
	ufinit(n+1);
	while(m--){
		scanf("%d%d",&a,&b);
		a=root(a);
		b=root(b);
		if(a!=b){
			if(unicnt[a]<unicnt[b]||unicnt[a]==unicnt[b]&&a>b)a^=b^=a^=b;
			uni(b,a);
		}
	}
	for(int i=1;i<=n;i++)printf("%d\n",root(i));
}

順番に縮めよう
あきらかにufinitとuni関数はいらない

r['~~'],c['~~'];
t(x){return r[x]-x?r[x]=t(r[x]):x;}

n,a,b,i,j;
main(){
	for(scanf("%d%*d",&n);i++<n;c[i]=1)r[i]=i;
	for(;~scanf("%d%d",&a,&b);a-b?c[a]<c[b]|c[a]==c[b]&a>b?a^=b^=a^=b:0,r[b]=a,c[a]+=c[b]:0)a=t(a),b=t(b);
	for(;j++<n;)printf("%d\n",t(j));
}

1つ目のfor文に登場するnは1e4に置き換えて良いので、2つあるscanfをまとめることができる

//before
for(scanf("%d%*d",&n);i++<n;c[i]=1)r[i]=i;
for(;~scanf("%d%d",&a,&b);a-b?c[a]<c[b]|c[a]==c[b]&a>b?a^=b^=a^=b:0,r[b]=a;c[a]+=c[b]:0)a=t(a),b=t(b);
//after
for(;i++<1e4;c[i]=1)r[i]=i;
for(;~scanf("%d%d",&a,&b);--x?a-b?c[a]<c[b]|c[a]==c[b]&a>b?a^=b^=a^=b:0,r[b]=a;c[a]+=c[b]:0:(n=a))a=t(a),b=t(b);
//ただしxはmain第一引数とする

あとは3つあるfor文を1つにまとめる

for(;i++<1e4;c[i]=1)r[i]=i;for(;~scanf("%d%d",&a,&b);--x?a-b?c[a]<c[b]|c[a]==c[b]&a>b?a^=b^=a^=b:0,r[b]=a,c[a]+=c[b]:0:(n=a))a=t(a),b=t(b);for(;j++<n;)printf("%d\n",t(j));
for(;i++<1e4?c[i]=1,r[i]=i:~scanf("%d%d",&a,&b)?a=t(a),b=t(b),--x?a-b?c[a]<c[b]|c[a]==c[b]&a>b?a^=b^=a^=b:0,r[b]=a,c[a]+=c[b]:1:(n=a):j++<n&&printf("%d\n",t(j)););

ということでまとめるとこう

r['~~'],c['~~'];
i,j,a,b,n;
t(k){return r[k]-k?r[k]=t(r[k]):k;}
main(x){for(;i++<1e4?c[i]=1,r[i]=i:~scanf("%d%d",&a,&b)?a=t(a),b=t(b),--x?a-b?c[a]<c[b]|c[a]==c[b]&a>b?a^=b^=a^=b:0,r[b]=a,c[a]+=c[b]:1:(n=a):j++<n&&printf("%d\n",t(j)););}

233B
return文省略は上手くいかず

yukicoder No.553 AlphaCoder Rating

問題はこちら
No.553 AlphaCoder Rating - yukicoder

ほとんどそのまま数式を計算すればよいが、g^(-1)とF(∞)だけは非自明。
gの逆関数はにぶたんで、F(∞)は適当に大きなxを用いてF(x)で近似することで、問題文の数式をそのまま実装して答えを得ることもできる。
以下では数学をやる解法を説明する。

g^(-1)(x)=800*log2(x)は明らか。
F(∞)については等比級数の和の公式
\sum_{i=1}^\infty r^i=\frac{r}{1-r}を用いて
F(\infty)=\dfrac{\sqrt{\frac{0.81}{1-0.81}}}{\frac{0.9}{1-0.9}}=\frac{1}{\sqrt{19}}が得られる

あとは、項を0.9倍ずつしながら足すという方法により、powを使うこと無く計算できる

n,t;
double r,de,nu,co,Fn,fn;
main(){
	scanf("%d",&n);
	r=0.9;
	while(n--){
		scanf("%d",&t);
		de+=r;
		nu+=r*pow(2,t/8e2);
		co+=r*r;
		r*=0.9;
	}
	Fn=sqrt(co)/de;
	fn=1200*(Fn-1/sqrt(19))/(1-1/sqrt(19));//F(1)=1
	printf("%.f",800*log2(nu/de)-fn);
}


このコードを基本にしつつ、nの読み飛ばしを工夫したり、f(n)を求める際の定数をいい感じにくくりだす事によって次が得られる

t;
float r,d,n,c;
main(i){
	for(;~scanf("%d",&t);r=--i?r:1)d+=r*=.9,c+=r*r,n+=r*pow(2,t/8e2);
	printf("%f",800*log2(n/d)+358-1557*sqrt(c)/d);
	//1557≒1200/(1-1/√19)、358≒1557/√19
}

136B
さて、ここでループ終了時にrに入っている値が0.9^nであることに注意しよう。
\sum_{i=1}^n 0.9^i=9*(1-0.9^n)
\sum_{i=1}^n 0.81^i=81/19*(1-0.81^n)
であることから、d=9*(1-r)、c=81/19*(1-r*r)であり、d,cの値はrから導出することもできる事がわかる
実際には、dは計算した方がよく、cはrから求めたほうが短くなる

t;
float r,n,d;
main(i){
	for(;~scanf("%d",&t);r=--i?r:1)d+=r*=.9,n+=r*pow(2,t/8e2);
	printf("%f",800*log2(n/d)+357-3213*sqrt(1-r*r)/d);
}

131B