首先考虑无解的情况,一个显然的观察是如果 R
的个数大于一半,那么无论如何都会出现两个 R
比赛的情况,小于一半时我们可以调整使得 B
全都在前面,显然有解。
接下来问题变为找到最优可行解,但是状态的合法性不是显然的,我们先尝试判定这个问题。
先考虑第一轮比赛,显然我们想让 B
获胜的尽可能多且编号尽可能靠前,因此,我们从左往右扫描序列,如果一个 B
还没有对手,那么从后面找到一个最接近的 R
当它的对手肯定最优,因为这样既能保证保留较小的 B
又能尽量把 较大的 R
留到后面,而最后没匹配到的无论怎么匹配结果肯定都一样。
这样对每一轮进行这个操作,如果过程中出现了不合法的情况即为无解,但是这样暴力进行判定并转移还是太慢了,现在要么优化转移要么优化判定,但这个题目在转移上没有什么特别好的性质,我们先尝试在判定方面进行优化。
考虑每一轮匹配的时候能不能不进行实际操作,只靠某些关键信息就能判定无解,由于上文我们提到在匹配完后没匹配到的无论怎么匹配结果都是一样,因此我们可以将某些 B
和 B
匹配的对子中最后一个 B
换成 R
,这样可以少考虑一个个数的因素。
剩下的就是位置的信息了,你发现第一个 B
靠前会优,而靠后到某一位置后可能就无解,其他的也一样,这个位置比较的过程其实有点像比较字典序,那存不存在一个最劣字符串使得比这个字符串大的字符串都可行呢?
实际上是存在的,但是由于这个消除的过程类似二分图匹配,这个大小关系的比较不是简单地比较字典序,而是考虑将 R
设为 -1,B
设为 1 并计算前缀和,我们要求前缀和每一位都大于等于最劣串的前缀和。
这个结论的充分性可以用归纳法证明,或者你可以感性地想象一下相消的过程,总之,这个字符串不仅存在,并且是易于通过递归求出的,当我们得到前一轮的最劣串时,由于每个 B
此时又可以解决一个 R
,因此我们在每个 B
后面紧跟一个 R
表示字典序最小,然后在最后补齐 B
就可以了。
答案的计算则是简单的,由于我们要前缀和全部大于等于,且一次交换能将前缀差值增加 2,因此只要计算不够的部分的和然后除以 2 即可。
#include<bits/stdc++.h>
#define ll long long
#define N 2000005
using namespace std;
ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9')f=(ch=='-'?-1:1),ch=getchar();
while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x*f;
}
void write(ll x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
ll a[N];
int main(){
ll n=read(),cnt=0;
string s="RB";
for(ll i=2;i<=n;i++){
string t="";
for(ll i=0;i<s.length();i++){
t+=s[i];if(s[i]=='B')t+='R';
}
while(t.length()<(1<<i))t+='B';
s=t;
}
string t;cin>>t;ll S=0;
for(ll i=0;i<(1<<n);i++)cnt+=(t[i]=='R');
if(cnt>(1<<(n-1))){cout<<-1;return 0;}
for(ll i=0;i<(1<<n);i++)a[i]+=(s[i]=='R'?-1:1),a[i]-=(t[i]=='R'?-1:1);
for(ll i=0;i<(1<<n);i++){
if(i!=0)a[i]+=a[i-1];
S+=(a[i]>0?a[i]:0);
}
cout<<S/2;
return 0;
}
标签:ch,匹配,前缀,题解,ll,ARC169E,判定,Boring,我们
From: https://www.cnblogs.com/eastcloud/p/17970806