A-AC
時間限制:C/C++ 1秒,其他語言2秒
空間限制:C/C++ 262144K,其他語言524288K
Special Judge, 64bit IO Format: %lld
題目描述
Crystal's fortune predict system is successfully developed!
The predict system is a distributed system consists of \(N\) computers. When it receives a predict request, each computer will generate one lowercase letter as its output. The final fortune predict result is determined by the concentration of all \(N\) outputs.
Tired of getting bad predictions like awful: \(\texttt{awful: millions of bugs}\), Ben decides to hack into the predict system and modify the predict result. He has already got the access permission of every computer in the predict system, so he can modify their output to any letter arbitrarily.
As Ben is going to take part in ICPC Asia Regional Kunming Site 2077, he wants predictions like \(\texttt{suitable for writing codes}\) or \(\texttt{will get accepted for every problem}\). He has found that the more times the substring \(\texttt{ac}\) occurs in the concentration of all \(N\) outputs, the luckier he will get in the contest. But as the contest is coming soon, he only has time to modify at most \(K\) outputs of the computers in the predict system.
As Ben is busy hacking into the system, could you tell him how to get the most \(\texttt{ac}\) substrings after his modification?
輸入描述
The first line contains two integers \(N\) and \(K\) \((1 \leq N \leq 5\times 10^5, 0 \leq K \leq N)\).
The second line contains a string of length \(N\), denoting the origin prediction. It is guaranteed that the string consists of lowercase English letters.
輸出描述
Output two lines. The first line contains a single integer, denotes the maximum number of \(\texttt{ac}\) substring Bob can get, after his modification. The second line contains the final modified predict string. If there are multiple ways that results in the maximum number of \(\texttt{ac}\) substring, print any.
示例1
輸入
9 2
arakbacca
輸出
3
acacbacca
題目大意
給定一個長度為\(N\)的字符串,可以修改\(K\)個字符。讓字符串中的\(\texttt{ac}\)盡可能地多。輸出修改后,最多的\(\texttt{ac}\)數量和 一種修改后的字符串。
題解
Problem A-AC
難度評價
Medium-hard
題解
考慮轉化問題:對於每個\(k \leq \frac{n}{2}\),如果我們能求出使得原串有\(k\)個ac
至少需要改變多少字符,那么就可以求解原題了。對每個\(i \in [1,n)\),我們計算\(c_i\)表示將原串第\(i\)位改成a
,第\(i+1\)位改成c
的代價,那么相當於是求解選擇\(k\)個\(c_i\),使得相鄰兩個\(c_i\)不同時被選的方案中,\(\sum c_i\)的最小值。這是一個經典的問題,可以運用堆維護可反悔的貪心來解決。構造方案根據選出的位置還原即可。時間復雜度\(O(n\;log\;n)\)
思路
思路來自 三個頂倆 的代碼
1.構造c[i]
代表第\(i-1\)位改為a
、第\(i\)位改為c
所需要修改的字符數量;
2.構造小根堆,以c[i]
為第一關鍵字,位置\(i\)為第二關鍵字;
3.l[i],r[i]
為當前位置相鄰字符的下標,L[i],R[i]
為當前實際修改的區間,即可設置邊界為r[0]=1,l[n]=n-1
;
4.對於每次操作,取出堆頂,檢查其是否已經被修改影響過(見7.)(used[now]
),直到找到一個沒有被修改過的位置,修改它,使 修改字符數cnt
\(+\)c[i]
,使 ac
數量ans
\(+1\),直到cnt>k
或者ans>n/2
(修改字符數超過規定 或 所有字符都盡可能變成了 ac
的形式);
5.之后便是往小根堆中更新反悔信息;
6.設used[i]
為當前位置是否被修改影響過(見7.),顯然對於4.的操作,每次都會使i,l[i],r[i]
受到影響;然后利用差分數組的性質,設vis[i]
代表 是否已經把字符串修改為 第\(i-1\)位為a
、第\(i\)位為c
的狀態;因為當前需要更新的為反悔信息,則設tmp
為新的操作的假位置,為了使其在小根堆的位置更靠后(因為反悔的代價通常很大,不如直接修改沒有修改過的地方);
7.如果當前修改區間的相鄰位置沒有到邊界,則顯然L[tmp]=L[l[i]],R[tmp]=R[r[i]]
,c[tmp]=c[l[i]]+c[r[i]]-c[i]
,即如果反悔,當前位置並不會變為ac
,則一定會讓左右兩側變為ac
,來保證ac
的數量得以增加;
舉例:
xacx
反悔后為acac
,這里如果對位置2(開始位置為0)進行反悔,則位置1和位置3都將受到影響(因為必須保證每次 從小根堆取出的位置 能進行操作 ,能讓ans
\(+1\);
8.隨后將 相鄰的 相鄰的 位置 與 當前操作的位置的l[tmp],r[tmp]
都進行一下連接,這樣,一個反悔操作就可以更新到 小根堆 中了;
並且,讓相鄰的 相鄰的 位置 直接 與 tmp 連接,可以防止后繼操作繼續更新
l[i],r[i]
位置,因為已經讓l[i],r[i]
作為反悔時更新的位置了,不能再單獨更新它們了;
9.特別地,如果已經到到了邊界,即當然位置沒得反悔了(左右相鄰的位置有一為邊界),則此位置就不能反悔,直接固定,直接將邊界延伸到當前范圍;
10.在遍歷答案時,可以發現,最多ac
的數量即為ans
,而任意一種字符串,可以根據差分數組vis[i]
,每個被修改的地方可以設為vis[L[i]]^=1,vis[R[i]+1]^=1
,通過差分數組的特點,更新其前綴和,如果更新后仍有vis[i]=1
,說明 第\(i-1\)位為a
、第\(i\)位為c
,強制修改即可;
cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define mp make_pair
#define fi first
#define se second
const int N=5e5+5;
using T=pair<int,int>;
priority_queue<T,vector<T>,greater<T>> q;
int n,k,tmp,ans,cnt,l[N<<1],r[N<<1],L[N<<1],R[N<<1],c[N<<1];
bool used[N<<1],vis[N];
string s;
void del(int i){
l[r[i]]=l[i];
r[l[i]]=r[i];
}
void orz(int i){
used[i]=used[l[i]]=used[r[i]]=1;
vis[L[i]]^=1;
vis[R[i]+1]^=1;
if(l[i]!=0 && r[i]!=n){
tmp++;
L[tmp]=L[l[i]];
R[tmp]=R[r[i]];
c[tmp]=c[l[i]]+c[r[i]]-c[i];
del(l[i]);
del(r[i]);
r[l[i]]=tmp;
l[r[i]]=tmp;
l[tmp]=l[i];
r[tmp]=r[i];
q.push(mp(c[tmp],tmp));
} else if(l[i]==0 && r[i]!=n){
del(r[i]);
del(i);
} else if(l[i]!=0 && r[i]==n){
del(l[i]);
del(i);
}
}
int main(){
scanf("%d %d",&n,&k);
cin>>s;
for(int i=1;i<n;++i){
c[i]=(s[i-1]!='a')+(s[i]!='c');
l[i]=i-1; r[i]=i+1;
L[i]=R[i]=i;
q.push(mp(c[i],i));
}
r[0]=1; l[n]=n-1; tmp=n;
while(ans<n/2){
T now;
do{
now=q.top();
q.pop();
}while(used[now.se]);
cnt+=now.fi;
if(cnt>k) break;
++ans;
orz(now.se);
}
for(int i=1;i<n;++i){
vis[i]^=vis[i-1];
if(vis[i])
s[i-1]='a',s[i]='c';
}
printf("%d\n",ans);
cout<<s;
return 0;
}