「JOIST2023-ビーバーの合唱(Chorus)」题解
ビーバーの合唱(Chorus)
sol
这道题如果常规思路做的话,按常理来说,在斜率优化时理应额外开一个队列储存所有不应被加入队列的决策点,但不加也可以过,然而按照常规思路解释为什么可以不开不是很容易。详见洛谷题解区的部分题解,这里就不详加讲解了。
考虑图像法,一个 A 就右移一格,B 就上移一格。序列合法的必要条件是路径必须位于对角线右下,详见下图。这个等价于任意前缀 A 的数量不应多于 B 的数量。初始序列可能有不合法的前缀,预处理一下使其合法即可,这是不劣的。具体地,记 \(c_i\) 为第 \(i\) 个 B 之前的 A 的数量,若存在 \(c_i<i\),必然交换 \(i-c_i\) 个 A 到当前点左侧。

上图中黑色的路径就是原序列(我们预处理之后得到的序列)对应的路径,考虑 \(k\) 段子序列的限制,首先每一段子序列显然都由第 \([l,r]\) 个 A 和第 \([l,r]\) 个 B 组成,可以调整法简单证明,然后有一个很妙的想法是,可以将 \(k\) 段子序列视作 \(k\) 个峰,如上图中红色路径就可以视作 \(2\) 个峰,也就分别对应两段子序列。
一个峰的简单定义为如上图,从一点 \((l,l)\) 一直往右走到 \((l,r)\) 然后再一直往上走到 \((r,r)\),等价于第 \([l,r]\) 个 A B 组成的一段合法子序列,满足所有 B 位于 A 右侧。
然后很妙的一点是,一种红色路径的代价为所有黑色路径之下红色路径之上的区域的面积,也就是上图中绿色部分。考虑交换一对相邻的 A B,在图中表现为将一个 \(1\times 1\) 的转折部分沿这个 \(1\times1\) 的对角线翻折,也就是原来先往右再往上变成先往上再往右,反之同理。那么绿色部分的面积就是把所有红线之上的黑线翻折到红线的代价。然后考虑红线之下的黑线为什么不用翻上去,这是因为我们只要求各段内满足这个性质,但各段在原序列上不一定连续,那么对于红线以下的黑线,它们就属于不同的几段(可以把竖线向左平移到最近的红线,横线向上平移到最近的红线,就是它们各自属于的段),因此它们之间的相互位置关系并不重要,并不需要进行修改,也能找到对应的子序列合法地分配它们。
因此我们可以对每一个峰计算出它的代价,也就是原路径在其之下的面积。这是简单的,对一个峰 \([l,r]\),考虑找到所有在左边界以内的竖线,然后差分一下求得面积即可。
如果不考虑峰的数量限制的话,上面就已经可做了,不难发现可以斜率优化,且横坐标与斜率均满足单调性,可以 \(O(n)\) 单调队列实现。然后考虑峰的限制,不难发现满足凸性,可以 WQS 二分,然后就没了。
code
const int N=2e6+5;
int n,k,ca,cb;
string s;
int c[N];
ll ans,pre[N],sum[N],cnt[N];
int q[N],hd,tl;
ll f[N],g[N];
ll X(int i){return i;}
ll Y(int i){return f[i]+pre[i-1]+i;}
inline bool chk(ll m){
q[hd=tl=1]=1;
rep(i,2,n+1){
while(hd<tl&&i*(X(q[hd+1])-X(q[hd]))>Y(q[hd+1])-Y(q[hd]))++hd;
f[i]=Y(q[hd])-X(q[hd])*i+(cnt[i-1]+1)*(i-1)-sum[i-1]+m,g[i]=g[q[hd]]+1;
while(hd<tl&&(Y(i)-Y(q[tl]))*(X(q[tl])-X(q[tl-1]))<(Y(q[tl])-Y(q[tl-1]))*(X(i)-X(q[tl])))--tl;
q[++tl]=i;
}
return g[n+1]<=k;
}
inline void Main(){
cin>>n>>k>>s;
s=' '+s;
rep(i,1,n<<1)
if(s[i]=='A')++ca;
else c[++cb]=ca;
rep(i,1,n)if(c[i]<i)ans+=i-c[i],c[i]=i;
rep(i,1,n)pre[i]=pre[i-1]+c[i],++cnt[c[i]],sum[c[i]]+=c[i];
rep(i,1,n)cnt[i]+=cnt[i-1],sum[i]+=sum[i-1];
ll l=0,r=1e12;
while(l<r){
ll m=l+r>>1;
if(chk(m))r=m;
else l=m+1;
}
chk(l);put(ans+f[n+1]-k*l);
}

浙公网安备 33010602011771号