博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

HDU.6681.Rikka with Cake(欧拉公式 树状数组)

题目链接


\(Description\)
\(n*m\)矩形内给定\(k\)条射线,求矩形内划分成的连通块数。

\(Solution\)
一个能看出来的结论:联通块数=交点数+1.
证明是用欧拉公式/定理:区域数R+顶点V-边数E=2.
设射线间交点数为\(c\),则总边数为\(4+射线数n+射线与矩形交叉产生n+射线交叉产生2c\)
总顶点数为\(4+射线本身n+与矩形相交产生n+c\)(任何一个点就是一个顶点)
所有区域数就是\(R=2+4+2n+2c-4-n-n-c=c+2\),矩形外也算一个区域所以答案为\(c+1\)

求交点数方法很多。
Sol1:
将垂直向下的射线和水平的射线按\(y\)排序,按顺序枚举,并对\(x\)建树状数组。水平射线就用两个(或一个)树状数组前缀或后缀+1;垂直射线就查一下单点的值。
然后再对垂直向上的射线求一遍就ok了。

Sol2:
将所有射线按\(y\)排序,按顺序枚举,并对\(x\)建树状数组。垂直射线出现时就用树状数组单点+1,消失时单点-1;水平射线就查一下前缀或后缀区间的值(后缀就是当前垂直射线总数-前缀的)。
(和Sol1一样的,不过用垂直射线修改只需要一遍。树状数组是\(x\)还是\(y\)也是一样的(就是区间修改还是单点修改的区别))

Sol3:
对于向下的射线\((xi,yi)\),求向左的射线\((xj,yj)\)中满足\(xj>xi\)\(yj<yi\)的射线个数,和向右的射线\((xj,yj)\)中满足\(xj<xi\)\(yj<yi\)的射线个数。
对于向上的射线,反转一下\(y\)坐标和水平射线的方向再求一次。
然后求解过程就是二位偏序,排序+树状数组即可。

代码为\(Sol2\)(好像都要\(2k\)次树状数组)。


//904MS	3148K
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=1e5+5;

int A[N];
struct Segment
{
	int type,x,y;//0:U 1:D 2:L 3:R
	bool operator <(const Segment &s)const
	{
		return y<s.y;
	}
}seg[N];
struct BIT
{
	int n,t[N];
	#define lb(x) (x&-(x))
	void Add(int p)
	{
		for(; p<=n; p+=lb(p)) ++t[p];
	}
	void Delete(int p)
	{
		for(; p<=n; p+=lb(p)) --t[p];
	}
	int Query(int p)
	{
		int res=0;
		for(; p; p^=lb(p)) res+=t[p];
		return res;
	}
}T;

inline int read()
{
	int now=0; char c=gc();
	for(; !isdigit(c); c=gc());
	for(; isdigit(c); now=now*10+c-48,c=gc());
	return now;
}
inline int Get()
{//0:U 1:D 2:L 3:R
	char c=gc(); for(; !isalpha(c); c=gc());
	return c=='U'?0:c=='D'?1:c=='L'?2:3;
}
inline int Find(int r,int x)
{
	int l=1,mid;
	while(l<r)
		if(A[mid=l+r>>1]<x) l=mid+1;
		else r=mid;
	return l;
}

int main()
{
	for(int Case=read(); Case--; )
	{
		int L=read(),R=read(),n=read();
		for(int i=1,x,y; i<=n; ++i)
			A[i]=x=read(), y=read(), seg[i]=(Segment){Get(),x,y};
		std::sort(A+1,A+1+n);
		int cnt=1;
		for(int i=2; i<=n; ++i) if(A[i]!=A[i-1]) A[++cnt]=A[i];
		for(int i=1; i<=n; ++i) seg[i].x=Find(cnt,seg[i].x);
		std::sort(seg+1,seg+1+n);

		int ans=1,sum=0; T.n=cnt;
		for(int i=1; i<=n; ++i) seg[i].type==1&&(T.Add(seg[i].x),++sum);
		for(int i=1; i<=n; ++i)
			switch(seg[i].type)
			{
				case 0: ++sum, T.Add(seg[i].x); break;
				case 1: --sum, T.Delete(seg[i].x); break;
				case 2: ans+=T.Query(seg[i].x); break;
				case 3: ans+=sum-T.Query(seg[i].x-1); break;
			}
		for(int i=1; i<=n; ++i) seg[i].type==0&&(T.Delete(seg[i].x),0);
		printf("%d\n",ans);
	}

	return 0;
}
posted @ 2020-09-27 17:52  SovietPower  阅读(132)  评论(0编辑  收藏  举报