题解 寿司
Description
小c是一名oier .
最近 , 他发现他的数据结构好像学傻了 . 因为他在刷题时碰到了一道傻逼数据结构题 , 强行使用了平衡树来解决 , 卡着时间AC . 为此 , 他被狠狠地嘲讽了一番 . 于是 , 小c找了大量的数据结构题来做 .
昨天 , 小c正在吃寿司 , 突然发现许多盘寿司围成了一个圆圈 , 这些寿司中有红色的也
有蓝色的 . 由于小c看交错的颜色非常不爽 , 想通过一些操作 , 使得所有的红色寿司形成了一块连续的区域 , 蓝色的寿司也形成了一块连续的区域 . 如果小c每次只可以交换相邻的两盘寿司 , 那么最少需要多少步才可以达到小c的要求呢 ? 由于他做题做多了 , 脑袋已经有点不清醒了 , 于是这个问题就交给你了 .
输入格式
第一行一个数
接下来
Solution
看到环 , 首先断环成链 , 对于一个序列来说 , 我们要做的就是把一种颜色放在两边 , 另一种颜色放在中间 .
因为一个蓝色在中间 , 红色在两边的方案也在其他序列中对应着一个红色在中间 , 蓝色在两边的方案 , 所以我们对一个序列只考虑红色在中间 , 蓝色在两边
设
那么这个序列的答案就是
其中
设
上式变为
由于
- 最后一个颜色为蓝色
一个值为Rnum 的x_i 的值变为-Rnum . - 最后一个颜色为红色
全体x 的值加2 .
也就是说 , 我们要写一种数据结构 , 支持
- 将一个
Rnum 的值变为-Rnum . - 全体加2
- 随时查询所有数的绝对值之和
考虑使用大根堆 , 维护
初始时我们将所有为负的
操作 1
使
操作 2
使
反复取出大根堆堆顶 , 若堆顶值加上
操作 3
维护的
所以我们一直将序列右移 , 并使用
复杂度
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
int read()
{
int ret=0;char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
return ret;
}
int abs(int x){return x>0?x:-x;}
const int maxn=1e6+5;
int T;
int n;
char s[maxn];
ll ans;
priority_queue<int>neg;
int val[maxn];
int main()
{
T=read();
while(T--)
{
scanf("%s",s+1);
n=strlen(s+1);
while(neg.size())neg.pop();
int now=0,rnum=0,bnum=0,num0=0,num1=0;
ll sum=0;
int delta=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='B')now++,rnum++;
else val[i]=now,bnum++;
}
now=0;
for(int i=n;i>=1;i--)
{
if(s[i]=='B')now++;
else
{
val[i]-=now;
if(val[i]<0)neg.push(val[i]),num1++;
else num0++;
sum+=abs(val[i]);
}
}
ll all=(ll)rnum*bnum;
ans=all-sum;
for(int i=n;i>=1;i--)
{
if(s[i]=='R')
{
num1++;num0--;
neg.push(-rnum-delta);
}
else
{
sum=sum+2ll*num0-2ll*num1;
delta+=2;
while(neg.size())
{
int now=neg.top();
if(now+delta>=0)
{
num1--;num0++;neg.pop();
if(now+delta==1)sum+=2;
}
else break;
}
ans=min(ans,all-sum);
}
}
printf("%lld\n",ans/2);
}
return 0;
}