ABC150解説
さて、今回から本格的にABCの解説記事を書いていきたいと思います。結果は70:09 (2ペナ) 26位でした。まあそれなりにむずかしかったし、悪くはないですがよくはないです。
A - 500 Yen Coins
思考
500円玉が枚あると、円になる。500円玉100枚ほしい人生だった。
実装例
#include<bits/stdc++.h> using namespace std; int main(){ int k,x; cin>>k>>x; cout<<(k*500>=x?"Yes":"No")<<endl; }
B - Count ABC
思考
が小さいので、番目の文字から文字見てABCになっているか確認するだけで良い。
実装例
#include<bits/stdc++.h> using namespace std; int main(){ int n; string s; cin>>n>>s; int c{}; for(int i=0;i<n-2;++i) if(s.substr(i,3)=="ABC") ++c; cout<<c<<endl; }
C - Count Order
思考
が小さいので、順列全部見ても8!=40320通りしかない。全ての順列を確認してP,Qと一致する奴が何番目か調べれば良い。
実装例
#include<bits/stdc++.h> using namespace std; int main(){ int n; cin>>n; vector<int> p(n),q(n),r(n); for(auto&i:p) cin>>i,--i; for(auto&i:q) cin>>i,--i; for(int i=0;i<n;++i) r[i]=i; int a{},b{},c{}; do{ bool f=true,g=true; fr(i,n){ if(p[i]!=r[i]) f=false; if(q[i]!=r[i]) g=false; } if(f) a=c; if(g) b=c; ++c; }while(next_permutation(r.begin(),r.end())); cout<<abs(a-b)<<endl; }
D - Semi Common Multiple
思考
がの半倍数というのは、であるということである。よってがの半公倍数であることは、中国剰余定理からなんかがあって、である(これは嘘で後で訂正します)。この時のが何かと考えると、で良いことがわかる(これはで考えたとき、2倍すると0になることからわかる)。よってさえ求めれば良い。
さてここから私はWAとTLEを1回ずつくらいます。まず先ほど嘘と言った話ですが、例えばとは両立しません。ちょっと考えると自分の2倍のmodが定まっていると、存在しません。それをいい感じにチェックします(詳しくは実装例を)(追記するかも)。
またこれはlcaを考えるならいつも気をつけなきゃいけないことですが、オーバーフローして色々やばいです(負の数のlcaを想定していなかった影響で無限ループとかしてTLEを食らったと思う)。なのでいい感じにチェックします(あまりに大きい時は、M以下で存在しないので打ち切ることができます)。
実装例
#include<bits/stdc++.h> using namespace std; template<class T> T gcd(T n,T m); int main(){ int n,m; cin>>n>>m; vector<ll> A(n); for(auto&i:A) cin>>i; sort(A.rbegin(),A.rend()); unsigned long long a,x=1,d; fr(i,n){ a=A[i]; if(x%a==0&&(x/a)%2==0) return cout<<0<<endl,0; d=gcd(a,x); if(x>2*m/(a/d)+1) return cout<<0<<endl,0; x*=a/d; } cout<<(m+x/2)/x<<endl; }
E - Change a Little Bit
思考
S,Tに対しての最小コストがどんなかを考える。これはどう考えてもが小さい方から取っていくのが良い。よってが最小の項は異なっていれば常に最初にとるし、が最大の項は異なっていれば常に最後にとることがわかる。それぞれの項にを変更する総コストを考えて、最後に足すと良さそう。を大きい順に並べ替えておく。
i番目の項の変更するコストを考える。i番目の項を最後からj番目に変更するような状況は、i番目の項が異なり、1〜i番目までで異なっているものがj-1個あるというような状況である。これは組み合わせを用いると簡単に計算できて、あとはWolfram Alphaに突っ込むととかで計算できる。あとは全てのiについて考えて足せば良い。
実装例
#include<bits/stdc++.h> using namespace std; struct modint{}; //Z/MOD Z modint pwr(int_fast64_t a,int_fast64_t b); //(a^b)%MOD int main(){ int n; cin>>n; vector<int> c(n); for(auto&i:c) cin>>i; sort(c.rbegin(),c.rend()); modint ans{}; for(int i=0;i<n;++i){ ans+=pwr(2,2*n-2)*(i+2)*c[i]; } cout<<ans<<endl; }
F - Xor Shift
思考
以下では添字は適当にで考えて下さい。
a'とbが等しくなるような(k,x)のがどのような条件を満たすか考えると、を任意のiについて満たすことがわかる(xorは自分自身が逆元になるため)。よってを満たすので、を満たすことがわかります。そのため、新たに,とすると、AをシフトさせてBと一致するようなkを考えれば良いことがわかる。kが複数ある場合は、Aが周期を持つので、あらかじめAの周期を調べておいて、kが最小のケースだけ考えれば良い*1。これは約数だけ見ればいいのでとなる(d(N)は約数個数関数)。これはなら十分高速。
あとはAをシフトさせてBと一致するようなkを考えればいいが、これは†Z-Algorithm†を使うとよくて、B+[-1]+A+A(ここで+は連結)というような数列についてZ-Algorithmを適用して、接頭辞とN文字共通しているところを探せば終わる*2。
実装例
#include<bits/stdc++.h> using namespace std; void zalgo(vector<ll>& s,vector<int>& z) //Z-algorithm int main(){ int n; cin>>n; vector<ll> a(n),b(n),A(n),B(n),c(3*n+1); for(auto&i:a) cin>>i; for(auto&i:b) cin>>i; fr(i,n) A[i]=a[i]^a[(i+1)%n]; fr(i,n) B[i]=b[i]^b[(i+1)%n]; vector<int> d; int s=n; for(int i=0;i<n;++i) c[i]=B[i]; c[n]=-1; for(int i=0;i<n;++i){ c[n+1+i]=A[i]; c[2*n+1+i]=A[i]; } vector<int> z; zalgo(c,z); for(int i=1;i<=n;++i){ if(n%i==0){ bool F=true; for(int j=0;j<i;++j){ bool f=true; ll C=A[j]; for(int k=0;k<n/i;++k) if(A[j+k*i]!=C) f=false; if(f) continue; F=false; break; } if(F){ s=i; break; } } } bool f=false; for(int i=n+1;i<=3*n;++i){ if(z[i]==n){ i-=n+1; while(i<n){ cout<<i<<" "<<(a[i]^b[0])<<endl; i+=s; } return 0; } } }
終わりに
ギリギリ間に合ったぜ!と30秒でABC終了です、お疲れ!