AGC030E Less than 3
阿新 • • 發佈:2020-10-04
AGC030E [* hard]
給的兩個長度為 \(n\) 的 01
串 \(s,t\),串的連續段長度不超過 \(2\),每次可以將 \(s\) 的一個位置反轉,要求操作後連續段長度仍然不超過 \(2\),求使得 \(s,t\) 相等的最小操作次數。
\(n\le 5000\)
Solution
考慮 \(s_i\ne s_{i+1}\) 的下標位置,假設 \(s_i=0\) 我們標記此處為 \(0\),否則標記為 \(1\)
例如 001001011
標記為 .01.010.1
我們不難發現 \(s\) 的標記序列是一個 01
交錯的形式。
然後我們發現一次操作其實是將一個標記往左/右移。
我們發現最後的目標其實是將所有標記對齊。
為了方便處理,我們強制讓標記的開頭為 \(0\)。
唯一的問題是對兩端進行操作時導致答案的改變並不滿足我們的規則。
所以我們給兩端補上 \(\infty\) 個交錯的標記。
我們發現操作序列合法等價於任意兩個相鄰的標記距離都不大於 \(2\)
我們發現答案的下界是將標記對齊後的下標差的和。
事實上,下界總是可以達到的,這是因為初始狀態合法,操作的過程中我們每次先抵著再走即可。
所以我們只需要列舉從 \(\infty\) 個交錯的標記中額外取出多少個標記即可,然後 \(\mathcal O(n)\) 計算答案,總體複雜度是 \(\mathcal O(n^2)\) 的。
要注意在一開始保證 \(s\)
\(Code:\)
#include<bits/stdc++.h> using namespace std ; #define Next( i, x ) for( register int i = head[x]; i; i = e[i].next ) #define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i ) #define drep( i, s, t ) for( register int i = (t); i >= (s); -- i ) #define re register int gi() { char cc = getchar() ; int cn = 0, flus = 1 ; while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; } while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ; return cn * flus ; } const int N = 5000 + 5 ; const int inf = 1e9 + 7 ; int n, g[N], f[N * 3], d[N * 3], cnt, num, Ans ; char s[N], t[N] ; signed main() { n = gi(), Ans = inf ; scanf("%s", s + 1 ) ; scanf("%s", t + 1 ) ; rep( i, 1, n ) s[i] -= '0', t[i] -= '0' ; if(t[1] == 1) g[++ num] = 0 ; //begin with 0 rep( i, 1, n - 1 ) if(t[i] != t[i + 1]) g[++ num] = i ; if(t[n] == 1) g[++ num] = n ; int u = (num / 2) * 2 ; rep(i, 1, u) f[++ cnt] = 0 ; if(s[1] == 1) f[++ cnt] = 0 ; rep( i, 1, n - 1 ) if(s[i] != s[i + 1]) f[++ cnt] = i ; if(s[n] == 1) f[++ cnt] = n ; rep( i, 1, num ) f[++ cnt] = n ; for(re int i = 0; i <= cnt - num; i += 2) { int ans = 0 ; rep( j, 1, i ) d[j] = 0 ; rep( j, i + 1, i + num ) d[j] = g[j - i] ; rep( j, i + num + 1, cnt ) d[j] = n ; rep( j, 1, cnt ) ans += abs(f[j] - d[j]) ; Ans = min( Ans, ans ) ; } cout << Ans << endl ; return 0 ; }