cf1860f
萌萌题,但是细节比较麻烦。
首先注意到,
设我们只关心
那么如何判断一个括号序列合法呢?将左括号看作
通过以上分析,原问题化为:给定若干条直线,第
这是一个简单的问题。注意到
可以考虑将每一个交点处理出来。排序,从左往右处理每一个交点,并且动态维护前缀和即可。
详细地说:
根据直线求交公式:
然后记录一下这两条直线的编号。将这些交点排序,依次处理。
处理时,我们动态维护每个直线的排名
最后判断全局最小值是否小于零即可。
前缀和的维护可以使用线段树,执行区间修改和最小值维护。
说一些细节和实现上的技巧:
-
在最初的时候,我们以
做一次排序为最初的 ,这里可以方便地直接将所有直线排序。排序规则比较复杂,首先按 自小到大, 相同的时候按照 自小到大排序。若 仍然相同,说明两直线重合,此时按照其权值贪心地自大到小即可。 -
在求交的时候需要注意通过对
的排序,我们只需要枚举 ,但需要满足 。注意是严格大于。 -
为了保证精度,交点的横坐标可以存下分子分母,否则可能
需要设置的非常小才可以通过,比如 。 -
在对点排序时,先按照
坐标排序,若相同则按照左右两端点的权值排序。需要注意的是右端点优先左端点,原因是右端点在交换后排名更靠前。 -
在修改点的时候,若遇到
的情况,可以直接略去。这种情况是很巧妙的解决。此时会有多线共交,两个端点在与其他直线操作的时候,就已经更改了排名,此处是满足了更改后
的情况,故不用操作。事实上,这里它潜在地完成了一次排序操作,即将排名倒转。
如果不进行该操作会在第三个点出错。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 3050
struct line{
int k,b,w;
bool operator<(const line t)const {
return b==t.b?(k==t.k?w>t.w:k<t.k):b<t.b;
}
}a[N];
struct node{
int l,r,lz,mn;
}t[N<<2];
#define lc x<<1
#define rc x<<1|1
void pushdown(int x){
if(t[x].lz!=0){
t[lc].mn+=t[x].lz;t[lc].lz+=t[x].lz;
t[rc].mn+=t[x].lz;t[rc].lz+=t[x].lz;
t[x].lz=0;
}
}
void pushup(int x){
t[x].mn=min(t[lc].mn,t[rc].mn);
}
void build(int x,int l,int r){
t[x].l=l,t[x].r=r,t[x].mn=t[x].lz=0;
if(l==r)return ;
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
pushup(x);
}
void change(int x,int l,int r,int k){
if(l<=t[x].l&&t[x].r<=r){
t[x].mn+=k,t[x].lz+=k;return ;
}
pushdown(x);
if(t[lc].r>=l)change(lc,l,r,k);
if(t[rc].l<=r)change(rc,l,r,k);
pushup(x);
}
#define db double
struct point{
int p,q,l,r;
bool operator<(const point b)const {
return p*b.q==q*b.p?(a[r].w==a[b.r].w?a[l].w<a[b.l].w:a[r].w>a[b.r].w):p*b.q<q*b.p;
}
}p[N*N];
int tot,rk[N];
signed main(){
ios::sync_with_stdio(false);
int T;cin>>T;
while(T--){
int n;cin>>n;tot=0;
n<<=1;build(1,1,n);
for(int i=1;i<=n;i++){
cin>>a[i].k>>a[i].b;char c;
cin>>c;a[i].w=(c==')'?-1:1);
rk[i]=i;
}
sort(a+1,a+n+1);build(1,1,n);
for(int i=1;i<=n;i++)change(1,i,n,a[i].w);
for(int i=1;i<=n;i++)
for(int j=n;j>i;j--)
if(a[i].k>a[j].k)p[++tot]=(point){a[j].b-a[i].b,a[i].k-a[j].k,i,j};
sort(p+1,p+tot+1);int tag=0;
if(t[1].mn>=0){
cout<<"Yes\n";continue;
}
for(int i=1;i<=tot;i++){
int x=p[i].l,y=p[i].r;
if(rk[x]>rk[y])continue;
change(1,rk[x],n,-a[x].w);
change(1,rk[y],n,-a[y].w);
swap(rk[x],rk[y]);
change(1,rk[x],n,a[x].w);
change(1,rk[y],n,a[y].w);
if(t[1].mn>=0){
tag=1;break;
}
}
if(tag)cout<<"Yes\n";
else cout<<"No\n";
}
}
// 2 9
// x+7,2x+5,4x+1
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!