WC2012 记忆中的水杉树
有诗意的名字,但这码量也太大了吧。
第一个想法就是我们可以对上下、左右建立两个DAG。从连向表示在删掉前,你要把删掉才行。
直接建图是的,肯定过不了。
这里我们就要使用扫描线:比如对于上下方向,我们用一条的垂直直线,从左往右扫。当我们加入一个线段时,找到第一个大于它的线段和第一个小于它的线段,从连向,从连向。
这样图的边数降为。
但是有一个问题:我们如何找到第一个大于它的线段和第一个小于它的线段?直接用set貌似不行,因为坐标在变化。但是其实是可以的,因为题中保证线段之间没有交点,所以尽管在变化,但是每条线段的相对顺序不会交换,仍然满足平衡树的性质。
此时第二问就很简单了,对上下方向的,按照拓扑序从小到大,并且每次操作都是向下平移即可。
对于第一问,我们可以从后往前做,找到四个方向对应的区间(左开右开),查找对应的区间内部拓扑值是否有小于或大于它的,这样,我们只要再维护两个支持区间修改min&max
和区间查询min&max
的线段树即可。
时间复杂度为。
写了行,最后还附上一个针对第二问的HACK。
#include<bits/stdc++.h>
#define debug(...) std::cerr<<#__VA_ARGS__<<" : "<<__VA_ARGS__<<std::endl
const int maxn=400005,inf=1000000000;
int n,mx,sz,a[maxn],b[maxn],c[maxn],d[maxn];
std::vector<int> nums,add[maxn],del[maxn];
int number(int num) {
return std::lower_bound(nums.begin(),nums.end(),num)-nums.begin()+1;
}
int now;
struct line {
double k,b;
int id;
bool operator == (const line &x) const {
return id==x.id;
}
bool operator < (const line &x) const {
return 1.0*now*k+b<1.0*now*x.k+x.b;
}
}tmp[maxn];
std::vector<int> G[2][maxn];
void doit(int id) {
std::set<line> S;
for(int i=1;i<=mx;i++) {
now=nums[i-1];//注意这里不能是离散化后的,也就是不能写出now=i,详见HACK1
for(auto item : del[i]) {
S.erase(tmp[item]);
}
for(auto item : add[i]) {
std::set<line>::iterator it=S.lower_bound(tmp[item]);
if(it!=S.begin()) {
it--;
G[id][it->id].push_back(item);
it++;
}
if(it!=S.end()) {
G[id][item].push_back(it->id);
}
S.insert(tmp[item]);
}
add[i].clear(); del[i].clear();
}
}
int in[maxn],per[2][maxn];
void toposort(int id) {
memset(in,0,sizeof in);
for(int i=1;i<=n;i++) {
for(auto nxt : G[id][i]) {
in[nxt]++;
}
}
std::queue<int> q;
int cur=0;
for(int i=1;i<=n;i++) {
if(!in[i]) q.push(i);
}
while(!q.empty()) {
int pos=q.front(); q.pop();
per[id][pos]=++cur;
for(auto nxt : G[id][pos]) {
in[nxt]--;
if(!in[nxt]) q.push(nxt);
}
}
}
int p[maxn],q[maxn],seq[maxn];
struct sgt {
int mn[maxn+maxn<<2],mx[maxn+maxn<<2],lzy_mn[maxn+maxn<<2],lzy_mx[maxn+maxn<<2];
void build(int pos=1,int lef=1,int rig=sz) {
mn[pos]=lzy_mn[pos]=inf,mx[pos]=lzy_mx[pos]=-inf;
if(lef==rig) return;
int mid=lef+rig>>1;
build(pos<<1,lef,mid);
build(pos<<1|1,mid+1,rig);
}
void GetNewMin(int pos,int num) {
mn[pos]=std::min(mn[pos],num);
if(num!=inf) mx[pos]=std::max(mx[pos],num);
lzy_mn[pos]=std::min(lzy_mn[pos],num);
}
void GetNewMax(int pos,int num) {
if(num!=-inf) mn[pos]=std::min(mn[pos],num);
mx[pos]=std::max(mx[pos],num);
lzy_mx[pos]=std::max(lzy_mx[pos],num);
}
void pushdown(int pos) {
GetNewMin(pos<<1,lzy_mn[pos]);
GetNewMin(pos<<1|1,lzy_mn[pos]);
GetNewMax(pos<<1,lzy_mx[pos]);
GetNewMax(pos<<1|1,lzy_mx[pos]);
lzy_mn[pos]=inf,lzy_mx[pos]=-inf;
}
void pushup(int pos) {
mn[pos]=std::min(mn[pos<<1],mn[pos<<1|1]);
mx[pos]=std::max(mx[pos<<1],mx[pos<<1|1]);
}
void update_mn(int l,int r,int num,int pos=1,int lef=1,int rig=sz) {
if(l<=lef&&rig<=r) {
GetNewMin(pos,num);
} else if(l<=rig&&lef<=r) {
pushdown(pos);
int mid=lef+rig>>1;
update_mn(l,r,num,pos<<1,lef,mid); update_mn(l,r,num,pos<<1|1,mid+1,rig);
pushup(pos);
}
}
void update_mx(int l,int r,int num,int pos=1,int lef=1,int rig=sz) {
if(l<=lef&&rig<=r) {
GetNewMax(pos,num);
} else if(l<=rig&&lef<=r) {
pushdown(pos);
int mid=lef+rig>>1;
update_mx(l,r,num,pos<<1,lef,mid); update_mx(l,r,num,pos<<1|1,mid+1,rig);
pushup(pos);
}
}
int query_mn(int l,int r,int pos=1,int lef=1,int rig=sz) {
if(l<=lef&&rig<=r) {
return mn[pos];
} else if(l<=rig&&lef<=r) {
pushdown(pos);
int mid=lef+rig>>1;
int ret=std::min(query_mn(l,r,pos<<1,lef,mid),query_mn(l,r,pos<<1|1,mid+1,rig));
pushup(pos);
return ret;
}
return inf;
}
int query_mx(int l,int r,int pos=1,int lef=1,int rig=sz) {
if(l<=lef&&rig<=r) {
return mx[pos];
} else if(l<=rig&&lef<=r) {
pushdown(pos);
int mid=lef+rig>>1;
int ret=std::max(query_mx(l,r,pos<<1,lef,mid),query_mx(l,r,pos<<1|1,mid+1,rig));
pushup(pos);
return ret;
}
return -inf;
}
}lr,ud;
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
nums.insert(nums.end(),{a[i],b[i],c[i],d[i]});
}
std::sort(nums.begin(),nums.end());
nums.erase(std::unique(nums.begin(),nums.end()),nums.end());
mx=nums.size();
for(int i=1;i<=n;i++) {
int from=number(a[i]),to=number(c[i]);
if(from>to) std::swap(from,to);
add[from].push_back(i);
del[to].push_back(i);
tmp[i].k=(double)(b[i]-d[i])/(double)(a[i]-c[i]);
tmp[i].b=(double)b[i]-(double)a[i]*tmp[i].k;
tmp[i].id=i;
}
doit(0);
for(int i=1;i<=n;i++) {
int from=number(b[i]),to=number(d[i]);
if(from>to) std::swap(from,to);
add[from].push_back(i);
del[to].push_back(i);
tmp[i].k=(double)(a[i]-c[i])/(double)(b[i]-d[i]);
tmp[i].b=(double)a[i]-(double)b[i]*tmp[i].k;
tmp[i].id=i;
}
doit(1);
toposort(0);
toposort(1);
for(int i=1;i<=n;i++) {
scanf("%d%d",&p[i],&q[i]);
}
sz=mx+mx;
lr.build();
ud.build();
int ans;
for(int i=n;i>=1;i--) {
{
//l and r
int from=number(b[p[i]])*2,to=number(d[p[i]])*2;
if(from>to) std::swap(from,to); to--;
if(q[i]==0&&lr.query_mn(from,to)<per[1][p[i]]) {
ans=i;
}
if(q[i]==2&&lr.query_mx(from,to)>per[1][p[i]]) {
ans=i;
}
lr.update_mn(from,to,per[1][p[i]]);
lr.update_mx(from,to,per[1][p[i]]);
}
{
//u and d
int from=number(a[p[i]])*2,to=number(c[p[i]])*2;
if(from>to) std::swap(from,to); to--;
if(q[i]==3&&ud.query_mn(from,to)<per[0][p[i]]) {
ans=i;
}
if(q[i]==1&&ud.query_mx(from,to)>per[0][p[i]]) {
ans=i;
}
ud.update_mn(from,to,per[0][p[i]]);
ud.update_mx(from,to,per[0][p[i]]);
}
}
printf("%d\n",ans);
for(int i=1;i<=n;i++) seq[per[0][i]]=i;
for(int i=1;i<=n;i++) {
printf("%d 3\n",seq[i]);
}
return 0;
}
/*
Hack1
8
0 1 2 2
3 3 1 0
2 0 4 2
5 3 2 4
4 3 6 1
4 4 7 5
6 2 7 3
7 1 6 0
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话