問題はこちら
No.671 1000000007 - yukicoder
文字列として受け取って、その長さがどれだけ違うかを見るのが一番簡単。
s;main(){printf("%d",abs(strlen(gets(&s))-10));}
48B
問題はこちら
No.671 1000000007 - yukicoder
文字列として受け取って、その長さがどれだけ違うかを見るのが一番簡単。
s;main(){printf("%d",abs(strlen(gets(&s))-10));}
48B
この記事では遅延セグメント木の「気持ち」を説明します。
通常のセグメント木に関する知識を前提とします。
アルゴリズムの説明のみであり、実装については一切説明していません。
基本的には以下のサイトの記述を、自分好みにリライトしたものです。
beet-aizu.hatenablog.com
○遅延セグメント木とは
半群
作用素モノイドの部分モノイド
の写像
があり、次の(1)~(3)の条件を満たすとする。
(1) の演算、作用素の適用、作用素の合成はいずれもで行える
(2) はにもにも依らずで計算できる
(3) *1
このとき上の長さの列に対する2種類のクエリ(i)(ii)を遅延セグメント木によってで処理することができる
(i)区間更新クエリ:区間と作用素が与えられ、全てのについてをに更新する
(ii)区間計算クエリ:区間が与えられ、を求める
○実現する方法
通常のセグメント木では区間の積の情報を持っていたが、遅延セグメント木ではそれに加え、その区間全体に作用する作用素の情報を持つ。
これだけ。
○でクエリが処理できることの確認
各nodeが長さをの区間の情報をの形で持っているとする。
・区間更新クエリ
区間と作用素が与えられたとする。上から順に次のように各nodeを更新する。
(1)のとき
をに更新する
(2)のとき
何もしない
(3)それ以外のとき
次の3つのことを行う
1.を子nodeに伝播する。即ち、子nodeがであるとき、これをに更新する
(↑これが肝となる遅延伝播)
2.子nodeを再帰的に更新する
3.子nodeの更新結果を元に自身のnodeが持つべき値を計算し、に更新する
以上の操作は個のnodeをで更新するのでとなる。
・区間計算クエリ
区間が与えられたとする。上から順に次のように、各nodeを更新しながら値を計算する
(1)のとき
を返す
(2)それ以外のとき
次の3つのことを行う
1.を子nodeに伝播する。即ち、子nodeがであるとき、これをに更新する
2.自身のnodeを、に更新する
(更新クエリと違って値が変化しないので子を見る必要がない)
3.子nodeを再帰的に呼び出して値を計算したものを返す
以上の操作は個のnodeをで更新/計算するのでとなる。
○自明な例
・区間加算クエリ+区間和クエリ
・区間加算クエリ+区間maxクエリ
・区間変更クエリ+区間和クエリ
・区間変更クエリ+区間maxクエリ
○非自明な例1
の数列が与えられる。次のクエリに答えよ
(i)区間加算クエリ:区間と値が与えられ、全てのについてをに更新する
(ii)区間変更クエリ:区間と値が与えられ、全てのについてをに更新する
(iii)区間計算クエリ:区間が与えられ、を求める
(が写像の合成で閉じていることを確かめよ)
○非自明な例2
の数列とが与えられる。次のクエリに答えよ
(i)区間更新クエリ:区間と値が与えられ、全てのについてをに更新する
(ii)区間計算クエリ:区間が与えられ、を求める
○非自明な例3
とする。上の演算を
により定める。
の数列が与えられる。次のクエリに答えよ
(i)区間更新クエリ:区間と値が与えられ、全てのについてをに更新する
(iii)区間計算クエリ:区間と値が与えられ、を求める
○余談1
仮定(2)を「はに依らずで計算できる」と弱めるとクエリ当たりとなる。
ただし、そのかわりに次の条件を仮定するとで計算可能である。
(2)' が単射かつはに依らずで計算できる
この場合、各nodeはの形で情報を持てばよい。
例えば上で挙げた7つの例は全て(2),(2)'の仮定をともに満たす。
(2)と(2)'の仮定をともに満たす場合、各nodeの情報をの形で持つ流儀の人と、の形で持つ流儀の人がいるため、他人の説明/コードを読む際は、どちらの流儀で書かれたものが注意する必要がある。
○余談1の余談
(2)と(2)'は独立な条件か?
・「(2)を満たし(2)'を満たさないもの」の例としては、区間乗算+区間総積クエリなどがある。
なので、のときを考えるとこれは単射ではない。
しかしこれはで考えることで単射とみなすことができる。
(のへの作用はとする)
本質的にダメな例があったら教えて
・「(2)'を満たし(2)を満たさないもの」の例は思いついてないので誰か教えて
○余談2
上で挙げた例は、全てを適切に取り直すことで、とすることができる。
そうすることが出来ない例があったら教えて
*1: よく考えると課すべき条件はT(xy)=(f(T)x)(f(T)y)だったが、ここを変更すると記事全てを書き直す必要があること、現在の条件でも整合性はあること、の2点を理由に修正は行わない
問題はこちら
https://yukicoder.me/problems/1975
yukicoder No.657 テトラナッチ数列 Easy - メモと全く同じ
long n; f(a,b,c,d){n--%4912?f(b,c,d%17,a+b+c+d):printf("%d\n",a);} main(i){for(;~scanf("%ld",&n);--i&&f(1,0,0,0));}
113B
問題はこちら
No.657 テトラナッチ数列 Easy - yukicoder
T0=1として、n=0から数列が始まるとしてよい。
フィボナッチ数列同様、直前の4項を持って漸化式で求めることができる。
直前の4項は17^4=83521状態なので、高々83521の周期を持つ。
具体的に計算することで4912の周期をもつことがわかるので逐次計算すれば良い。
int main(){ int q; scanf("%d",&q); while(q--){ long n; scanf("%ld",&n); n%=4912; int a=1,b=0,c=0,d=0; while(n--){ int t=(a+b+c+d)%17; a=b; b=c; c=d; d=t; } printf("%d\n",a); } }
この手のものはループでやるか再帰でやるか悩むが、代入が多いので再帰でやったほうがよさそう。
出力まで再帰関数のなかでやってしまうことにするといろいろ噛み合う。
main再帰より通常の再帰の方が1B短い
i,n; main(a,b,c,d){n--%4912<1?++i>2&&printf("%d\n",d),~scanf("%d",&n)&&main(0,0,0,1):main((a+b+c+d)%17,a,b,c);}
n; f(a,b,c,d){n--%4912?f(b,c,d,(a+b+c+d)%17):printf("%d\n",a);} main(i){for(;~scanf("%d",&n);--i&&f(1,0,0,0));}
これで完成……といいたいところだが、%17をつける位置を第4引数から第3引数に変更することでカッコを外せる。
これにより第4引数は再帰のたびに大きくなることになるが、a+b+c<51なので1回の再帰で高々51しか増加せず、再帰回数は4912回以下なのでオーバーフローの心配はない
n; f(a,b,c,d){n--%4912?f(b,c,d,(a+b+c+d)%17):printf("%d\n",a);} main(i){for(;~scanf("%d",&n);--i&&f(1,0,0,0));}
107B
問題はこちら
No.656 ゴルフ - yukicoder
やるだけ
int main(){ int s=0; for(int i=0;i<9;i++){ int c=getchar()-48; if(c==0)s+=10; else s+=c; } printf("%d",s); }
1文字ずつ見るタイプの問題は、getcharとreadどちらを使うか、最後の改行をどうするか、といったことを考える必要がある。
とりあえずgetchar()-48?:10 という方法をすぐ思いつくが、実はもっと短い方法がある。
'0'が'9'より大きくなって欲しいので、49で剰余をとればよい。このとき'1'~'9'は0~8になるので、'0'は9になってほしい。なのでさらに39で剰余をとる。
これらを合計すると答えより9小さい値が出てくるが、'\n'の10が足されるので答えより1大きくなる。よって正負を反転させてbit反転すればよい。
s;main(i){for(;i=getchar()-10;)s+=i-38?:10;printf("%d",s);} s;main(i){for(;i=~getchar();)s-=i%50%40;printf("%d",s-2);} s;main(i){for(;read(0,&i,1);)s-=i%49%39;printf("%d",~s);}
57B