密钥题解
题目大意
给定一个初始01串S和一个目标01串T,每次操作可以将0->1或者1->0。每次代价是过程S中每个1的代价和,也就是说每次操作S中的1都会产生代价,要求最小代价
思路分析
因为过程中每个1都会产生代价,所以想法就是先把S中需要1->0的先进行操作,然后需要0->1的后操作,前者降序,后者升序,显然这样的代价是最小的。
我们来讨论下什么样的01需要操作
-
S[i]==1~and~T[i]==0 这位必须操作且只能操作一次
-
S[i]==0~and~T[i]==1 同上也必须操作
-
S[i]==0~and~T[i]==0 肯定不会去操作,操作只会更劣势
-
S[i]==1~and~T[i]==1 乍一看不需要进行任何操作,但别忘了,过程中的每个1都会产生代价!
这时有位大聪明就想到了,”我这种情况都进行操作不就行了“,然后你就得到WA20了,(别问我是怎么知道的,问就是我就是WA20)
所以我们就要枚举这类的操作情况的,因为要操作的话也可能会更劣势。光枚举时间肯定不过关的,我们发现这类情况我们要先操作代价比比较大的,
但后面代价小的不一定需要操作,因为多一次操作就会多一次代价!,可以想到枚举前缀。时间复杂度 O(n^2)
具体实现就是先统计必须要操作的序列,然后枚举1 1这种情况,可以用优先队列维护,但这样不方便挨个访问每个值,每次操作需要一个一个取出才行,毕竟堆只有堆顶最优,所以我们可以使用平衡树!都知道,这玩意可以拿STL里的vector实现(万能的vector),代码非常简洁,长度就那WA20代码差不多,当然用优先队列和set之类也行咧。维护,当然写过平衡树板子的人
真·核心代码
void solve(){
for(int i=0;i<seq.size();i++){//枚举前缀
int sum=0;
fr.insert(lower_bound(fr.begin(),fr.end(),seq[i]),seq[i]);//1->0的序列
ba.insert(lower_bound(ba.begin(),ba.end(),seq[i]),seq[i]);//0->1的序列
for(int j=i+1;j<seq.size();j++)sum+=seq[j]*(fr.size()+ba.size());//不操作序列的代价
for(int j=0;j<fr.size();j++) sum+=(fr.size()-j-1)*fr[j];//1->0
for(int j=0;j<ba.size();j++) sum+=(ba.size()-j)*ba[j];//0->1
if(sum<ans) ans=sum;//更新最小代价
}
}
别说我题解发AC代码了,这次总行了吧(恼