首页 > 其他分享 >P3316 [SDOI2014] 里面还是外面 题解

P3316 [SDOI2014] 里面还是外面 题解

时间:2024-12-19 20:54:20浏览次数:3  
标签:rt ch int 题解 P3316 Edge SDOI2014 inline x1

实现有些傻瓜,喜提时空双最劣解。

首先要判断一个点是否在多边形内,一个比较好的方法是从这个点向上引一条射线,若和奇数条边相交就在多边形内,否则在多边形外。

二维信息,考虑用树套树维护。把多边形的每一条边都扔到它 \(x\) 坐标范围的线段树节点里,即线段树节点 \((l,r)\) 里面维护了 \(x\) 坐标一端点小于 \(l\),一端点大于 \(r\) 的边。 由于边不相交,所以一个节点中的所有线段是有偏序关系的,用平衡树维护。查询的时候直接查询其 \(x\) 坐标对应节点到线段树根路径上的所有平衡树,直接计算在它上面的线段数量即可。

下面是一些细节:

如图,对于经过一个顶点的情况,前两种情况奇偶性不改变,而第三种情况改变了。因此我们可以将所有线段设成左闭右开的形式(注意这里不是按顺时针顺序,而是按 \(x\) 坐标大小区分左右)。

但如果这样的话,上图第一种情况中,如果询问的点就在右面的尖上,那么因为线段是左闭右开的那么它就不会被正确判断成在边界上。这个也好处理,用 map 存一下多边形上所有点,特判即可。

再就是关于比较两个线段(或比较线段和点)的上下关系,我写的是直接带两边的点值进去,带两个是因为有可能有两个线段有公共点的情况。

最后,本题卡空间,不要开太大数组。原则上需要离散化,但是我没离散化也过了。

代码

#include<bits/stdc++.h>
inline void rd(){}
template<typename T,typename ...U>
inline void rd(T &x,U &..args){
	int ch=getchar();
	T f=1;x=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	x*=f;rd(args...);
}
#define pii std::pair<int,int> 
#define mkp std::make_pair
const int N=5e4+5,INF=1e9;
const double eps=1e-9;
int n,m;
struct node{
	int x,y;
	node(){x=y=0;}
	node(int _x,int _y){x=_x,y=_y;}
	bool friend operator<(node x,node y){return x.x==y.x?x.y<y.y:x.x<y.x;}
}p[N];
struct Edge{
	int x1,x2,y1,y2;
	Edge(){x1=x2=y1=y2=0;};
	Edge(node a,node b){
		if(a.x>b.x)std::swap(a,b);
		x1=a.x,x2=b.x,y1=a.y,y2=b.y;}
	Edge(int _x1,int _y1,int _x2,int _y2){x1=_x1,x2=_x2,y1=_y1,y2=_y2;}
	inline double Val(int x){return 1.L*(y2-y1)*(x-x1)/(x2-x1)+y1;}
	bool friend operator<(Edge x,Edge y){
		int pos1=std::max(x.x1,y.x1),pos2=std::min(x.x2,y.x2);
		return x.Val(pos1)+eps<y.Val(pos1)||x.Val(pos2)+eps<y.Val(pos2);
	}
	bool friend operator<=(Edge x,Edge y){
		int pos1=std::max(x.x1,y.x1),pos2=std::min(x.x2,y.x2);
		return x.Val(pos1)<y.Val(pos1)+eps||x.Val(pos2)+eps<y.Val(pos2)+eps;
	}
	bool friend operator<(Edge x,node y){
		return x.Val(y.x)+eps<1.0*y.y;
	}
	bool friend operator==(Edge x,node y){
		return x.Val(y.x)-1.0*y.y<=eps;
	}
}v[N];
std::mt19937 mtrd(time(0));
namespace BST{
	int ch[N*60][2],sz[N*60],pri[N*60],cnt;
	Edge v[N*60];
	struct BST{
		int rt;
		BST(){rt=0;}
		inline int NewNode(Edge x){
			pri[++cnt]=mtrd();
			sz[cnt]=1;v[cnt]=x;
			return cnt;
		}
		inline void PushUp(int i){sz[i]=sz[ch[i][0]]+sz[ch[i][1]]+1;}
		int Merge(int x,int y){
			if(!x||!y)return x+y;
			if(pri[x]<pri[y]){
				ch[x][1]=Merge(ch[x][1],y);
				return PushUp(x),x;
			} 
			else{
				ch[y][0]=Merge(x,ch[y][0]);
				return PushUp(y),y;
			}
		}
		void Split(int now,Edge k,int &x,int &y){
			if(!now)return x=y=0,void();
			if(v[now]<k)x=now,Split(ch[now][1],k,ch[now][1],y);
			else y=now,Split(ch[now][0],k,x,ch[now][0]);
			PushUp(now);
		}
		void Split(int now,node k,int &x,int &y){
			if(!now)return x=y=0,void();
			if(v[now]<k)x=now,Split(ch[now][1],k,ch[now][1],y);
			else y=now,Split(ch[now][0],k,x,ch[now][0]);
			PushUp(now);
		}
		void Split(int now,int k,int &x,int &y){
			if(!now)return x=y=0,void();
			if(sz[ch[now][0]]>=k)y=now,Split(ch[now][0],k,x,ch[now][0]);
			else x=now,Split(ch[now][1],k-sz[ch[now][0]]-1,ch[now][1],y);
			PushUp(now);
		}
		inline void Insert(Edge x){
			int a,b;
			Split(rt,x,a,b);
			rt=Merge(Merge(a,NewNode(x)),b);
		}
		inline Edge Find(int x){
			while(ch[x][0])x=ch[x][0];
			return v[x];
		}
		inline int Get(node x){
			int a,b;
			Split(rt,x,a,b);
			int sum=sz[b];
			Edge tmp=Find(b);
			if(tmp==x)sum+=1e7;
			rt=Merge(a,b);
			return sum;
		}
		inline void Delete(Edge x){
			int a,b,c;
			Split(rt,x,a,b);Split(b,1,b,c);
			rt=Merge(a,c);
		}
	};
}
namespace SegT{
	struct Seg{
		BST::BST t;
		int ls,rs;
	}t[N*45];
	int cnt=0,rt=0;
	void Update(int &i,int l,int r,int L,int R,Edge k){
		if(!i)i=++cnt;
		if(L<=l&&r<=R)return t[i].t.Insert(k),void();
		int mid=(l+r)>>1;
		if(L<=mid)Update(t[i].ls,l,mid,L,R,k);
		if(R>mid)Update(t[i].rs,mid+1,r,L,R,k);
	}
	void Delete(int &i,int l,int r,int L,int R,Edge k){
		if(!i)i=++cnt;
		if(L<=l&&r<=R)return t[i].t.Delete(k),void();
		int mid=(l+r)>>1;
		if(L<=mid)Delete(t[i].ls,l,mid,L,R,k);
		if(R>mid)Delete(t[i].rs,mid+1,r,L,R,k);
	}
	int Query(int i,int l,int r,node x){
		if(!i)return 0;
		if(l==r)return t[i].t.Get(x);
		int mid=(l+r)>>1;
		if(x.x<=mid)return Query(t[i].ls,l,mid,x)+t[i].t.Get(x);
		else return Query(t[i].rs,mid+1,r,x)+t[i].t.Get(x);
	}
}
std::map<node,int> ndmp; 
inline void Add(Edge x){SegT::Update(SegT::rt,0,INF,x.x1,x.x2-1,x);}
inline void Del(Edge x){SegT::Delete(SegT::rt,0,INF,x.x1,x.x2-1,x);}
int q[3][2],ans,lstx,lsty;
signed main(){
	rd(n);
	for(int i=1;i<=n;i++)rd(p[i].x,p[i].y),ndmp[p[i]]=1;
	for(int i=1;i<=n;i++){
		node nd1=p[i],nd2=p[i%n+1];
		if(nd1.x>nd2.x)std::swap(nd1,nd2);
		v[i].x1=nd1.x,v[i].x2=nd2.x;
		v[i].y1=nd1.y,v[i].y2=nd2.y;
		Add(v[i]);
	}
	rd(m);
	if(ndmp[node(0,0)])ans=2;
	else ans=1;
	for(int i=1;i<=m;i++){
		int op,r,sum=0;node a,b,c;
		rd(op);
		if(op==0){
			rd(r);
			for(int i=0;i<3;i++)rd(q[i][0],q[i][1]);
			lstx=(1ll*lstx*r+q[ans][0])%INF;
			lsty=(1ll*lsty*r+q[ans][1])%INF;
			if(ndmp[node(lstx,lsty)])ans=2;
			else{
				sum=SegT::Query(SegT::rt,0,INF,node(lstx,lsty));
				if(sum>1e7)ans=2;
				else if(sum&1)ans=0;
				else ans=1;
			}
			puts((ans==2?"ed":(ans==1?"out":"in")));
		}else{
			rd(a.x,a.y,b.x,b.y,c.x,c.y);
			Del(Edge(a,b));Add(Edge(a,c));Add(Edge(b,c));
			ndmp[c]=1;
		}
	}
	return 0;
}

标签:rt,ch,int,题解,P3316,Edge,SDOI2014,inline,x1
From: https://www.cnblogs.com/11-twentythree/p/18617918

相关文章

  • 洛谷 P11337 「COI 2019」IZLET 题解
    如果每条边连接的两个点颜色都不相同,那么可以使用如下策略确定每个点的颜色:令\(c_{i,j}\)为\(i\)到\(j\)的路径上的颜色数。对于每个点\(u\),以其为根进行一次dfs,往下找直到找到一个和它颜色相同的或者一个叶子就回溯,如果遇到颜色相同的就将它们在并查集上合并。考虑如何......
  • [Yandex Cup 2024 Qual F] Zeroth Rome 题解
    前言Englishversionofthiseditorialisprovidedafterthesamplecode.题意简述本题为交互题,你需要猜\(t\)个\([0,2024]\)间的非负整数\(x_1,x_2,\ldots,x_t\),可以询问最多\(15\)次,每次询问形如“给定一个大小为\(N(1\leN\le2025)\)的集合\(S\)满足\(S\)......
  • [APC001H] Generalized Insertion Sort 题解
    将\(a_i\)视为放在结点\(i\)上面的球;称位置\(i\)对应的球为\(i\),区别于“位置\(i\)上面的球为\(a_i\)”。考虑树是一条链的时候怎么做(下称链插入方法):此时只需要将这条链上面的球按照编号从上到下排序。这是一个类似插入排序的过程,维护深度最大的的若干个球编号的相对顺......
  • [USACO24OPEN] Grass Segments G 题解
    考虑对于一个区间\([l_i,r_i]\),最少重叠长度为\(k_i\),怎样的区间\([l_j,r_j]\)可以与前者产生贡献;首先\(r_j-l_j\gek_i\),在满足这个条件的情况下需要有\(r_j\gel_i+k_i\landl_j\ler_i-k_i\),这里\(\land\)表示合取,即C++中的\(\mathrm{and}\)。正难则反,考虑用长度\(......
  • 题解:P10483 小猫爬山
    思路第一眼我以为是个背包,但由于是分组,所以有多个缆车,明显不能用背包。我做这题是因为老师要求,那是我们在学深搜减枝,所以我就开始写深搜。这一题实际上是先选一直最重的猫,然后搞个\(sum\)数组,每搞一个新缆车的就下一个下标继续放,如果能放就放,当然也要搞一个能放但不放的。减枝......
  • RHCE环境公共问题解决(9.0)
    关于如何使用远程软件进行连接环境问题查看此处网络适配器模式,如果是NAT请修改为仅主机模式Vmware有两张网卡,一张是Vmware1,一张是Vmware8(环境必须用仅主机,避免环境判分错误)默认情况下,仅主机模式修改Vmware1网卡 打开Windows的网络适配器可以看到两张网卡 环境IP为1......
  • AT_agc030_f [AGC030F] Permutation and Minimum 题解
    先去掉相邻两个都填完的位置,对于两个都没填的记个数为\(c\),最后只需要将答案乘上\(c!\)。接下来考虑从小到大枚举所有数进行dp,记\(f_{i,j,k}\)表示考虑完前\(1\simi\),有\(j\)个数需要跟一个位置确定的数匹配,有\(k\)个数需要跟后面一个自由的数匹配,考虑当前的数:如果......
  • AT_agc009_d [AGC009D] Uninity 题解
    一道妙妙题。一句话题意:求点分树的高度最小值。给所有点填上一个数表示它子树\(k\),考虑一种填法什么时候满足条件,发现当且仅当任意两对值相同的点之间的路径上存在一个权值更大的点。考虑随便找一个点作为根从叶子节点开始贪心填值,每次选择能填的最小值,发现这样填最终的值的最......
  • 题解:最优硬币组合问题
    更多算法题的题解见:算法刷题题解汇总(持续更新中)一、问题背景小C有多种不同面值的硬币,每种硬币的数量是无限的。他希望知道,如何使用最少数量的硬币,凑出给定的总金额N。小C对硬币的组合方式很感兴趣,但他更希望在满足总金额的同时,使用的硬币数量尽可能少。例如:小C有三种硬币......
  • 题解:P11409 西湖有雅座
    题解:P11409西湖有雅座题目转送带简洁思路由于数据比较小,可以先预处理出任何两个零件是否能出现在同一栋大楼上。即枚举所有的两个零件,根据题意去模拟判断条件是否满足:\[\foralli,j\inU,f\left(i,j\right)\ge\lceil\frac{\min\left(S\left(i\right),S\left(j\righ......