[ABC229] G - Longest Y
見出し画像

[ABC229] G - Longest Y

syamashi

[Q] https://atcoder.jp/contests/abc229/tasks/abc229_g

にぶたん + 累積和で15msくらい。O(NlogN)。
1. それぞれの 'Y' について、0indexからのドット数 A[] をとる
2.  A[] の累積和 acc[] をとる
3. 要素数で二分探索。要素は連続して取る。つど、開始indexを全探索する。
swap回数は、選んだ要素の中央値との差分。

Q 入力例1
YY...Y.Y.Y.
2

1. 'Y' について、0index からのドット数におきかえる。
YY...Y.Y.Y.
00123344556
A[] = {0,0,3,4,5}

2. 累積和
acc[] = {0,0,3,7,12}

3. にぶたん。取得要素数で探索をする。
low = 1
high = 5+1 (A.size() まで解答になるよう、A.size()+ちょい までとる)
low が答えになる。

mid = (high + low)/2

・mid = 3 のとき // 3 要素をとるよ
開始indexを全探索する。
1) A[0]~A[2] = {0,0,3}
中央値は A[1] = 0
cost = abs(0-0) + abs(0-0) + abs(3-0) = 3
これは K=2より大きいのでダメ。

2) A[1]~A[3] = {0,3,4}
中央値はA[2] = 3
cost = abs(3-0) + abs(3-3) + abs(4-3) = 4 > 2
K=2より大きいのでダメ。

3) A[2]~A[4] = {3,4,5}
中央値はA[3] = 4
cost = abs(4-3) + abs(4-4) + abs(5-4) = 2
これは、K=2以下なのでOK。 low = 3 が解答候補。

low = 3
high = 6
・mid = 4 のとき
1) A[0]~A[3] = {0,0,3,4}
中央値は A[1] = 0  // べつに A[2] でもいい。 A[1] 以上 A[2] 以下ならなんでもいい。
cost = abs(A[1]-A[0]) + abs(A[1]-A[1]) + abs(A[2]-A[1]) + abs(A[3]-A[1]) = 7
これは K=2 より大きいのでダメ。

2) A[1]~A[4] = {0,3,4,5}
中央値は A[2] = 3  // べつに A[3] でもいい。 A[2] 以上 A[3] 以下ならなんでもいい。
cost = abs(A[2]-A[1]) + abs(A[2]-A[2]) + abs(A[3]-A[2]) + abs(A[4]-A[2]) = 6
これは K=2 より大きいのでダメ。

low = 3
high = 4
high - low = 1 で、差分が1以下になってしまったので終了。 low = 3 が解答。

Q. 要素数の偶奇でcostの取り方が違う?(切り口がまずいだけかも?)
目が痛くなるメモ

A.
きすう   acc[R] + acc[L-1] - acc[median] - acc[median-1]
ぐうすう acc[R] + acc[L-1] - acc[median]*2

数式をこねて見出した。それはそう。



A[] = {1,2,3,4,5,6}
acc[] = {1,3,6,10,15,21}
を考える。

・要素が 5 個の場合(きすう)
A[1]~A[5]をとるとする。{2,3,4,5,6}
中央値 median = 開始index + (要素数-1)/2 = 1 + (5-1)/2 = 3
mval = A[3] = 4

cost = mval-A[1] + mval-A[2] + mval-A[3] + A[4]-mval + A[5]-mval
                               ---------消える
     = mval + mval - mval - mval - A[1] - A[2] + A[4] + A[5]
     = - A[1] - A[2] + A[4] + A[5]
     = acc[5] + acc[0] - acc[median] - acc[median-1]
    // 543210 +   0    -     3210    -      210   =  54-21のできあがり



・要素が 4 個の場合(ぐうすう)
A[1]~A[4] をとるとする {2,3,4,5}
中央値 median = 開始index + (要素数-1)/2 = 1 + (4-1)/2 = 2
mval = A[2] = 3

cost = mval-A[1] + mval-A[2] + A[3]-mval + A[4]-mval
     = - A[1] - A[2] + A[3] + A[4]
     = acc[4] + acc[0] - acc[median] - acc[median]
    // 43210 +   0    -     3210    -     3210   =  43-21のできあがり

実装

int main(){
 cincout();
 
 string S;
 ll K;
 cin >> S >> K;
 ll N = S.size();
 vector<ll> A;
 ll dot = 0;
 rep(i, N){
   if (S[i] == 'Y') A.push_back(dot);
   else ++dot;
 }
 ll asz = A.size();
 if (asz == 0){
   cout << 0 << endl;
   return 0;
 }

 // 累積和
 ll acc[asz];
 acc[0] = A[0];
 rep(i, asz-1) acc[i+1] = acc[i] + A[i+1];
 
 // にぶたん
 ll low = 1;
 ll high = asz+10;
 while(high-low > 1){
   ll mid = (high+low)/2; // 要素数
   rep(i, asz-mid+1){ // 開始index
     ll med = i + (mid-1)/2; // 中央index
     ll r = i+mid-1;
     // ぐうすう acc[R] + acc[L-1] - 2*acc[med]
     // きすう acc[R] + acc[L-1]  - acc[med] -acc[med-1]
     ll cost = acc[r] - 2*acc[med];
     if (i>0) cost += acc[i-1];
     if (mid%2) cost += A[med];
     if (cost <= K){
       low = mid;
       break;
     }
   }
   if (low != mid) high = mid;
 }
 cout << low << endl;
}

https://atcoder.jp/contests/abc229/submissions/27555430
Q. 本番とけたの?
A. だめー。
にぶたんは最初から粘って考えてた。中身をどうとるか迷った、セグ?累積?いもす?距離間の距離でとってみる?
結局、距離間の距離でしゃくとりかもって思ったけど沼。
お風呂入ったら中央値選択が思いついた

可能性高そうな最初の直感は、きっとあってる。諦めないで。

この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
তরকারীও রয়েছে
syamashi
非エンジニア。atcoder(highest:1844)/42Tokyo2月1期(pass)/セミリタイア(20190702最終出社)