CF1685C Bring Balance 题解
写篇题解记录一下学长讲的为数不多能听懂的题
如果觉得废话太多可以直接看末尾结论
遇到括号序列、
题目中说
我们的目标便是将所有低于
而我们能够进行的操作是区间反转,假设我们反转如下红色线段围住的区间:
黑色是反转前的线段,绿色是反转后的线段,可以看出,对应点及其高度均关于两点连线(紫色线段)的中点中心对称, 也就是每次反转区间
每次将某些低于
考虑反转了区间
(其中相同颜色标注的线段相等)
我们有:
即:
__也就是说当
结论:
也就是说若能找到一个
那如果不能一次搞定呢?那肯定是全部 化敌为友 ,直接反转
然后献上又臭又长的代码qwq:
#include<cstdio>
#include<iomanip>
#define INF 0x7f7f7f7f
using namespace std;
const int N=2e5+50;
char s[N];
int h[N],pm,pk[N],ed1,ed2;
//pm记录最高点,pk记录所有低于x轴的点
int n,t;
inline int Max(int a,int b){return a>b?a:b;}
pair<int,int> Work(int l,int r)//计算区间的最大值
{
int maxn=-INF,pos=0;
for(int i=l;i<=r;i++){
if(h[i]>maxn){
maxn=h[i],pos=i;
}
}
return make_pair(maxn,pos);
}
bool Check()
{
int ml=0,mr=0,mm=0;
pair<int,int> l,r,mid;
//注意这里h[i]表示i的前缀和,因此0才表示第一个括号,下面输出左区间+1也是同理
l=Work(0,pk[1]);
mid=Work(pk[1],pk[ed2]);
r=Work(pk[ed2],n*2);
if(l.first+r.first>=mid.first){//有区间满足条件
printf("1\n");
printf("%d %d\n",l.second+1,r.second);
return true;
}
return false;
}
void Print()
{
if(!ed2){//原序列为合法序列,不需要反转
printf("0\n");
return ;
}
if(!Check()){//若没有区间满足条件
printf("2\n");
printf("1 %d\n%d %d\n",pm,pm+1,2*n);
}
}
void Pre()//初始化点的高度
{
ed1=ed2=0;
for(int i=1;i<=n*2;i++){
if(s[i]=='(') h[i]=h[i-1]+1;
else h[i]=h[i-1]-1;
}
}
void Solve()
{
scanf("%d",&n);
scanf("%s",s+1);
Pre();
int maxn=-INF;//注意有的点可能小于0,因此 maxn 不能设为0,上同
for(int i=1;i<=n*2;i++){
if(h[i]>=maxn){
pm=i;
maxn=h[i];
}
if(h[i]<0) pk[++ed2]=i;
}
}
int main()
{
scanf("%d",&t);
while(t--){
Solve();
Print();
}
return 0;
}
然后就讲完力(喜)