競プロ参加日記015 AtCoder Beginner Contest 179(ABC179)
・はじめに
A,B,C,Eの4完でした。
DのDPの書き方が変でバグが取れなくて、時間を溶かしてしましました...。コンテスト後に、Dのバグがすぐとれたし、Fは一瞬で通せたで消化不良です...。
・A - Plural Form
問題文通りにif文を作ればOKです。
#include<bits/stdc++.h>
//#include<atcoder/all>
using namespace std;
//using namespace atcoder;
int main(){
string s;
cin>>s;
if(s[s.size()-1]=='s') cout<<s<<"es"<<endl;
else cout<<s<<"s"<<endl;
}
・B - Go to Jail
Di1==Di2ならカウントを増やして、Di1!=Di2ならカウントを0にする。ということを、i=0~N-1までやります。
こうすることで、カウント=連続ゾロ目の回数となるので、カウントが3以上になったタイミングでフラグ等を立てればOKです。
#include<bits/stdc++.h>
//#include<atcoder/all>
using namespace std;
//using namespace atcoder;
int main(){
int N;
cin>>N;
int c=0;
bool f=false;
for(int i=0;i<N;i++){
int D1,D2;
cin>>D1>>D2;
if(D1==D2) c++;
else c=0;
if(c==3) f=true;
}
if(f) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
・C - A x B + C
N=A+B+Cの問題(ABC085Cのこれとか)です。競プロでは多分典型です。
A,Bの2値が決まれば、C=N-A*Bの右辺が全て定数となるため、Cが一意に決まります。なので、A,Bの全パターンの中でCが題意に満たしているかを調べればよいです。
これだけだとO(N^2)っぽさそうですが、C>0であることを考えると、A,Bの取りうる範囲は凄く小さく、あり得ないケースをカットしていけば十分早くなります。
#include<bits/stdc++.h>
//#include<atcoder/all>
using namespace std;
//using namespace atcoder;
int main(){
int N;
int c=0;
cin>>N;
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
if(i*j>=N) break;
c++;
}
}
cout<<c<<endl;
}
・D - Leaping Tak
今回の戦犯問題。
範囲なのでセグ木を使いましょう(想定解は違う)。
セグ木を使って丁寧にDPをすると、O(NlogN)くらいで求まります。
#include<bits/stdc++.h>
//#include<atcoder/all>
using namespace std;
//using namespace atcoder;
//https://algo-logic.info/segment-tree/
/* SegTreeLazyProportional<X,M>(n,fx,fa,fm,fp,ex,em): モノイド(集合X, 二項演算fx,fa,fm,p 単位元ex,em)についてサイズnで構築
set(int i, X x), build(): i番目の要素をxにセット。まとめてセグ木を構築する。O(n)
update(i,x): i 番目の要素を x に更新。O(log(n))
query(a,b): [a,b) 全てにfxを作用させた値を取得。O(log(n))
*/
template <typename X, typename M>
struct SegTreeLazyProportional {
using FX = function<X(X, X)>;
using FA = function<X(X, M)>;
using FM = function<M(M, M)>;
using FP = function<M(M, int)>;
int n;
FX fx;
FA fa;
FM fm;
FP fp;
const X ex;
const M em;
vector<X> dat;
vector<M> lazy;
SegTreeLazyProportional(int n_, FX fx_, FA fa_, FM fm_, FP fp_, X ex_, M em_)
: n(), fx(fx_), fa(fa_), fm(fm_), fp(fp_), ex(ex_), em(em_), dat(n_ * 4, ex), lazy(n_ * 4, em) {
int x = 1;
while (n_ > x) x *= 2;
n = x;
}
void set(int i, X x) { dat[i + n - 1] = x; }
void build() {
for (int k = n - 2; k >= 0; k--) dat[k] = fx(dat[2 * k + 1], dat[2 * k + 2]);
}
/* lazy eval */
void eval(int k, int len) {
if (lazy[k] == em) return; // 更新するものが無ければ終了
if (k < n - 1) { // 葉でなければ子に伝搬
lazy[k * 2 + 1] = fm(lazy[k * 2 + 1], lazy[k]);
lazy[k * 2 + 2] = fm(lazy[k * 2 + 2], lazy[k]);
}
// 自身を更新
dat[k] = fa(dat[k], fp(lazy[k], len));
lazy[k] = em;
}
void update(int a, int b, M x, int k, int l, int r) {
eval(k, r - l);
if (a <= l && r <= b) { // 完全に内側の時
lazy[k] = fm(lazy[k], x);
eval(k, r - l);
} else if (a < r && l < b) { // 一部区間が被る時
update(a, b, x, k * 2 + 1, l, (l + r) / 2); // 左の子
update(a, b, x, k * 2 + 2, (l + r) / 2, r); // 右の子
dat[k] = fx(dat[k * 2 + 1], dat[k * 2 + 2]);
}
}
void update(int a, int b, M x) { update(a, b, x, 0, 0, n); }
X query_sub(int a, int b, int k, int l, int r) {
eval(k, r - l);
if (r <= a || b <= l) { // 完全に外側の時
return ex;
} else if (a <= l && r <= b) { // 完全に内側の時
return dat[k];
} else { // 一部区間が被る時
X vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
X vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
return fx(vl, vr);
}
}
X query(int a, int b) { return query_sub(a, b, 0, 0, n); }
};
int main(){
int n,k;
cin>>n>>k;
using X = long long;
using M = long long;
auto fx = [](X x1, X x2) -> X { return (x1 + x2)%998244353; };
auto fa = [](X x, M m) -> X { return (x + m)%998244353; };
auto fm = [](M m1, M m2) -> M { return (m1 + m2)%998244353; };
auto fp = [](M m, long long n) -> M { return (m * n)%998244353; };
long long ex = 0;
long long em = 0;
SegTreeLazyProportional<X, M> rmq(n, fx, fa, fm, fp, ex, em);
for(int i=0;i<n;i++){
rmq.set(i,0);
}
rmq.set(0,1);
int l[k],r[k];
for(int i=0;i<k;i++){
cin>>l[i]>>r[i];
}
for(int j=0;j<n;j++){
for(int m=0;m<k;m++){
int l2=l[m]+j;
if(l2>=n) continue;
int r2=r[m]+j;
if(r2>=n) r2=n-1;
int nn=rmq.query(j,j+1);
if(nn!=0){
rmq.update(l2,r2+1,nn);
}
}
}
cout<<rmq.query(n-1,n)<<endl;
}
※https://algo-logic.info/segment-tree/
こちらのセグ木をお借りしました。
セグ木に乗せられるのがモノイドと呼ばれる変なものだけらしく、MODはモノイドじゃないのでセグ木じゃ解けないのでは???みたいなことを考えて、setとかの解法をやってたら時間が解けました。(ACしたとこを見ると、MODでも大丈夫そうですね。)
・E - Sequence Sum
MODがある場合はループを疑えばよく、この問題もループします。
ex.10 2 1001 の場合、
2 → 4 → 16 → 256 → 471 → 620 → 16 → 256 → 471 → 620
となり、3~6,7~10が同じです。
f(x,m)のxがmodの影響で0<=x<mの範囲内のどれかになるため、少なくともm回以内に同じ数字が出現します。(ループし始めます)
setか何かで出現を管理して、ループを感知したら(同じ数字が出たら)、残り可能なループ回数分*ループで増える分を足すことで、シミュレーション回数を大幅に減らして高速に計算できます。
#include<bits/stdc++.h>
//#include<atcoder/all>
using namespace std;
//using namespace atcoder;
int main(){
long long N,X,M;
cin>>N>>X>>M;
set<long long> s;
long long A=X;
s.insert(A);
long long sum=A;
N--;
while(N>0){
A=A*A%M;
if(s.find(A)!=s.end()){
long long sum2=A;
long long c=1;
long long A2=A;
while(1){
A2=A2*A2%M;
if(A2==A) break;
c++;
sum2+=A2;
//cout<<c<<","<<sum2<<endl;
}
sum+=sum2*(N/c);
//cout<<N<<","<<c<<","<<sum2<<endl;
N%=c;
s.clear();
if(N==0) break;
}
s.insert(A);
sum+=A;
//cout<<A<<endl;
N--;
}
cout<<sum<<endl;
}
・F - Simplified Reversi
セグ木で殴る(想定解法じゃないっぽいです)。
各行の一番左の白コマ、各列の一番上の白コマをセグ木で管理すれば、白コマを置いたときに、何個ひっくり返せるかが分かるようになります。
#include<bits/stdc++.h>
//#include<atcoder/all>
using namespace std;
//using namespace atcoder;
//https://algo-logic.info/segment-tree/
/* SegTreeLazy<X,M>(n,fx,fa,fm,ex,em): モノイド(集合X, 二項演算fx,fa,fm, 単位元ex,em)についてサイズnで構築
set(long long i, X x), build(): i番目の要素をxにセット。まとめてセグ木を構築する。O(n)
update(i,x): i 番目の要素を x に更新。O(log(n))
query(a,b): [a,b) 全てにfxを作用させた値を取得。O(log(n))
*/
template <typename X, typename M>
struct SegTreeLazy {
using FX = function<X(X, X)>;
using FA = function<X(X, M)>;
using FM = function<M(M, M)>;
long long n;
FX fx;
FA fa;
FM fm;
const X ex;
const M em;
vector<X> dat;
vector<M> lazy;
SegTreeLazy(long long n_, FX fx_, FA fa_, FM fm_, X ex_, M em_)
: n(), fx(fx_), fa(fa_), fm(fm_), ex(ex_), em(em_), dat(n_ * 4, ex), lazy(n_ * 4, em) {
long long x = 1;
while (n_ > x) x *= 2;
n = x;
}
void set(long long i, X x) { dat[i + n - 1] = x; }
void build() {
for (long long k = n - 2; k >= 0; k--) dat[k] = fx(dat[2 * k + 1], dat[2 * k + 2]);
}
/* lazy eval */
void eval(long long k) {
if (lazy[k] == em) return; // 更新するものが無ければ終了
if (k < n - 1) { // 葉でなければ子に伝搬
lazy[k * 2 + 1] = fm(lazy[k * 2 + 1], lazy[k]);
lazy[k * 2 + 2] = fm(lazy[k * 2 + 2], lazy[k]);
}
// 自身を更新
dat[k] = fa(dat[k], lazy[k]);
lazy[k] = em;
}
void update(long long a, long long b, M x, long long k, long long l, long long r) {
eval(k);
if (a <= l && r <= b) { // 完全に内側の時
lazy[k] = fm(lazy[k], x);
eval(k);
} else if (a < r && l < b) { // 一部区間が被る時
update(a, b, x, k * 2 + 1, l, (l + r) / 2); // 左の子
update(a, b, x, k * 2 + 2, (l + r) / 2, r); // 右の子
dat[k] = fx(dat[k * 2 + 1], dat[k * 2 + 2]);
}
}
void update(long long a, long long b, M x) { update(a, b, x, 0, 0, n); }
X query_sub(long long a, long long b, long long k, long long l, long long r) {
eval(k);
if (r <= a || b <= l) { // 完全に外側の時
return ex;
} else if (a <= l && r <= b) { // 完全に内側の時
return dat[k];
} else { // 一部区間が被る時
X vl = query_sub(a, b, k * 2 + 1, l, (l + r) / 2);
X vr = query_sub(a, b, k * 2 + 2, (l + r) / 2, r);
return fx(vl, vr);
}
}
X query(long long a, long long b) { return query_sub(a, b, 0, 0, n); }
};
int main(){
long long n,q;
cin>>n>>q;
using X = long long;
using M = long long;
auto fx = [](X x1, X x2) -> X { return min(x1, x2); };
auto fa = [](X x, M m) -> X { return min(x,m); };
auto fm = [](M m1, M m2) -> M { return min(m1,m2); };
long long ex = numeric_limits<long long>::max();
long long em = numeric_limits<long long>::max();
SegTreeLazy<X, M> rmqy(n-2, fx, fa, fm, ex, em);
SegTreeLazy<X, M> rmqt(n-2, fx, fa, fm, ex, em);
for(long long i=0;i<n;i++){
rmqy.set(i,n-2);
rmqt.set(i,n-2);
}
long long sum=(n-2)*(n-2);
for(long long i=0;i<q;i++){
long long f,x;
cin>>f>>x;
x-=2;
if(f==1){
long long a=rmqy.query(x,x+1);
sum-=a;
//cout<<a<<endl;
rmqt.update(0,a,x);
}
else{
long long a=rmqt.query(x,x+1);
sum-=a;
//cout<<a<<endl;
rmqy.update(0,a,x);
}
/*for(long long j=0;j<n-2;j++){
cout<<rmqt.query(j,j+1)<<" ";
}
cout<<endl;
for(long long j=0;j<n-2;j++){
cout<<rmqy.query(j,j+1)<<" ";
}
cout<<endl;*/
}
cout<<sum<<endl;
}
※https://algo-logic.info/segment-tree/
Dと同じく、こちらのセグ木をお借りしました。
セグ木分を除けば実装も軽く、考察も簡単な問題でした。
(想定解はややこしそう)
・おわりに
区間の文字を見ると頭がセグ木一色になるので、一回忘れたいですね???
この記事が気に入ったらサポートをしてみませんか?