P10763 [BalticOI 2024] Tiles

洛谷

很明显是一个扫描线问题,但是明显比大部分扫描线复杂,因为除了要预处理还需要大量的分析。

由于题目要求我们求出在 \(x\) 左边铺上满足要求的最大值,所以可以以纵轴平行的边作为扫描线。

由于每条边之间可能会有一些没有用的端点,在建边时需要对这些边进行一个合并操作,即把两个边加在一起。

由于要进行区间操作,还需要对于纵坐标的值进行离散化,这样预处理就完成了。

预处理代码:

struct L{
	int l,r,x,op;//记录下纵坐标上下端以及横坐标,op表示是增边还是减边
	bool friend operator<(L a,L b){
		return a.x<b.x;
	}
}line[200001];
void init(){
	cin>>n>>m;
	for(int i=0;i<n;i++)cin>>x[i]>>y[i];
	int now=1;
	for(int i=1;i<n;i++)if(x[i]==x[i-1])line[++cnt]={y[i-1],y[i],x[i]};
	if(x[n-1]==x[0])line[++cnt]={y[n-1],y[0],x[0]};
	for(int i=2;i<=cnt;i++){
		if(line[now].x==line[i].x)line[now].r=line[i].r;
		else line[++now]=line[i];
	}
	while(now&&line[now].x==line[1].x)line[1].l=line[now--].l;
	n=now;
	cnt=0;
	for(int i=1;i<=n;i++){
		tmp[++cnt]=line[i].l;
		tmp[++cnt]=line[i].r;
	}
	sort(line+1,line+n+1);
	sort(tmp+1,tmp+cnt+1);
	cnt=unique(tmp+1,tmp+cnt+1)-tmp-1;
	bool f;
	if(line[1].l>line[1].r)f=1;//由于顺时针还是逆时针不确定,排序后此处为最早加入的边,以此为标准
	else f=0;
	for(int i=1;i<=n;i++){
		if(line[i].l>line[i].r){
			line[i].op=f;
			swap(line[i].l,line[i].r);
		}else line[i].op=f^1;
		line[i].l=lower_bound(tmp+1,tmp+cnt+1,line[i].l)-tmp;
		line[i].r=lower_bound(tmp+1,tmp+cnt+1,line[i].r)-tmp-1;//由于需要区间操作,每一个数表示一个区间,r要减一,扫描线常见套路
	}
}

然后问题就是怎么样通过扫描线记录下答案了。

可以先分析一下什么情况下是合法的。

首先如果我将横坐标为偶数的边加入原本有横坐标为奇数的边,不可行,因为此时二者距离必定不为偶数。

其次如果某些边组成了奇数长度的连续区间,那么也不可行。

那么就可以考虑怎么解决了。

第一步,我们可以分别开两个线段树分别维护纵坐标不同奇偶的情况下组成的几段区间,加入线段前判断一下是否有不同奇偶的线在此范围内。

第二步,我们可以直接在线段树中维护有多少个奇数区间即可。

关于线段树的部分代码如下:

struct P{
	int len,l,r,s;
	bool f;
	P friend operator +(P a,P b){
		P c;
		c.len=a.len+b.len;
		c.f=a.f|b.f;
		c.s=a.s+b.s;
		if(a.l==a.len)c.l=a.l+b.l;
		else {
			c.l=a.l;
			if(b.l!=b.len)c.f|=a.r+b.l&1;
		}
		if(b.r==b.len)c.r=a.r+b.r;
		else c.r=b.r;
		return c;
	}
	P friend operator +(P a,int b){
		P c;
		c.len=a.len;
		c.l=c.r=c.s=a.len*b;
		c.f=0;
		return c;
	}
	bool check(){
		return (f|(l&1)|(r&1));
	}
};
struct ST{
	P c[800001];
	int tag[800001];
	#define ls p<<1
	#define rs p<<1|1
	void Tag(int p,int v){
		tag[p]=v;
		c[p]=c[p]+v;
	}
	void pushup(int p){
		c[p]=c[ls]+c[rs];
	}
	void pushdown(int p){
		if(tag[p]==-1)return;
		Tag(ls,tag[p]);
		Tag(rs,tag[p]);
		tag[p]=-1;
	}
	void build(int p,int l,int r){
		tag[p]=-1;
		if(l==r){
			c[p]={tmp[r+1]-tmp[l],0,0,0,0};
			return;
		}
		int mid=l+r>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		pushup(p);
	}
	void change(int p,int l,int r,int L,int R,int v){
		if(l>=L&&r<=R)return void(Tag(p,v));
		pushdown(p);
		int mid=l+r>>1;
		if(mid>=L)change(ls,l,mid,L,R,v);
		if(mid<R)change(rs,mid+1,r,L,R,v);
		pushup(p);
	}
	P query(int p,int l,int r,int L,int R){
		if(l>=L&&r<=R)return c[p];
		pushdown(p);
		int mid=l+r>>1;
		if(mid>=L&&mid<R)return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
		else if(mid<R)return query(rs,mid+1,r,L,R);
		else return query(ls,l,mid,L,R);
	}
	bool check(){
		return c[1].s!=0;
	}
}tr[2];

最后就是激动人心的统计答案环节了。

在可以统计答案时,还有一些要关注的。

首先,在之后还有相同的纵坐标值的时候,不可以统计答案,否则可能会产生误判。

其次,如果此时既有横坐标为奇数的,又有横坐标为偶数的,那么不能统计答案,因为这截止的横坐标确定后,这两种情况就不可能都取到值。

如果此时只有奇数,那么纵坐标也一定是奇数。

如果此时只有偶数,那么纵坐标也一定是偶数。

统计答案到最后,如果可行,直接输出最大值 \(m\) 即可。

总代码(统计答案在主函数):

#include<bits/stdc++.h>
using namespace std;
int n,m,x[200001],y[200001],cnt,tmp[200001];
struct L{
	int l,r,x,op;
	bool friend operator<(L a,L b){
		return a.x<b.x;
	}
}line[200001];
struct P{
	int len,l,r,s;
	bool f;
	P friend operator +(P a,P b){
		P c;
		c.len=a.len+b.len;
		c.f=a.f|b.f;
		c.s=a.s+b.s;
		if(a.l==a.len)c.l=a.l+b.l;
		else {
			c.l=a.l;
			if(b.l!=b.len)c.f|=a.r+b.l&1;
		}
		if(b.r==b.len)c.r=a.r+b.r;
		else c.r=b.r;
		return c;
	}
	P friend operator +(P a,int b){
		P c;
		c.len=a.len;
		c.l=c.r=c.s=a.len*b;
		c.f=0;
		return c;
	}
	bool check(){
		return (f|(l&1)|(r&1));
	}
};
struct ST{
	P c[800001];
	int tag[800001];
	#define ls p<<1
	#define rs p<<1|1
	void Tag(int p,int v){
		tag[p]=v;
		c[p]=c[p]+v;
	}
	void pushup(int p){
		c[p]=c[ls]+c[rs];
	}
	void pushdown(int p){
		if(tag[p]==-1)return;
		Tag(ls,tag[p]);
		Tag(rs,tag[p]);
		tag[p]=-1;
	}
	void build(int p,int l,int r){
		tag[p]=-1;
		if(l==r){
			c[p]={tmp[r+1]-tmp[l],0,0,0,0};
			return;
		}
		int mid=l+r>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		pushup(p);
	}
	void change(int p,int l,int r,int L,int R,int v){
		if(l>=L&&r<=R)return void(Tag(p,v));
		pushdown(p);
		int mid=l+r>>1;
		if(mid>=L)change(ls,l,mid,L,R,v);
		if(mid<R)change(rs,mid+1,r,L,R,v);
		pushup(p);
	}
	P query(int p,int l,int r,int L,int R){
		if(l>=L&&r<=R)return c[p];
		pushdown(p);
		int mid=l+r>>1;
		if(mid>=L&&mid<R)return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R);
		else if(mid<R)return query(rs,mid+1,r,L,R);
		else return query(ls,l,mid,L,R);
	}
	bool check(){
		return c[1].s!=0;
	}
}tr[2];
void init(){
	cin>>n>>m;
	for(int i=0;i<n;i++)cin>>x[i]>>y[i];
	int now=1;
	for(int i=1;i<n;i++)if(x[i]==x[i-1])line[++cnt]={y[i-1],y[i],x[i]};
	if(x[n-1]==x[0])line[++cnt]={y[n-1],y[0],x[0]};
	for(int i=2;i<=cnt;i++){
		if(line[now].x==line[i].x)line[now].r=line[i].r;
		else line[++now]=line[i];
	}
	while(now&&line[now].x==line[1].x)line[1].l=line[now--].l;
	n=now;
	cnt=0;
	for(int i=1;i<=n;i++){
		tmp[++cnt]=line[i].l;
		tmp[++cnt]=line[i].r;
	}
	sort(line+1,line+n+1);
	sort(tmp+1,tmp+cnt+1);
	cnt=unique(tmp+1,tmp+cnt+1)-tmp-1;
	bool f;
	if(line[1].l>line[1].r)f=1;
	else f=0;
	for(int i=1;i<=n;i++){
		if(line[i].l>line[i].r){
			line[i].op=f;
			swap(line[i].l,line[i].r);
		}else line[i].op=f^1;
		line[i].l=lower_bound(tmp+1,tmp+cnt+1,line[i].l)-tmp;
		line[i].r=lower_bound(tmp+1,tmp+cnt+1,line[i].r)-tmp-1;
	}
}
signed main(){
	init();
	tr[0].build(1,1,cnt-1);
	tr[1].build(1,1,cnt-1);
	int ans=0;
	for(int i=1;i<=n;i++){
		int l=line[i].l,r=line[i].r,now=(line[i].x&1);
		if(tr[now^1].query(1,1,cnt-1,l,r).s)break;
		tr[now].change(1,1,cnt-1,l,r,line[i].op);
		if(i==n||line[i].x!=line[i+1].x){
			if(tr[0].c[1].check()||tr[1].c[1].check())break;
			if(i==n){
				ans=m;
				break;
			}
			if(!tr[0].check()&&tr[1].check()){
				ans=line[i+1].x;
				if(!(ans&1))ans--;
			}
			if(tr[0].check()&&!tr[1].check()){
				ans=line[i+1].x;
				if(ans&1)ans--;
			}
		}
	}
	cout<<ans;
	return 0;
}

posted @ 2025-12-06 16:22  huhangqi  阅读(0)  评论(0)    收藏  举报
/*
*/