gmoj 3976. 【NOI2015模拟1.17】⑨

题目

https://gmoj.net/senior/#main/show/3976

题解

这道题目有不少解题的方法,但是大多比较麻烦。这里提供一种简捷的方法(改进自 x i a o q i xiaoqi xiaoqi大佬的解法)

为了方便,不妨先将所有点平移至第一象限。假设要求出的是简单多边形ABCDE中的青蛙贡献值,各顶点分布如下图所示:

在这里插入图片描述

n u m △ A B C num_{△ABC} numABC表示△ABC中青蛙的价值之和。
发现这是等于 n u m △ O A B + n u m △ O B C + n u m △ O C D − n u m △ O E D − n u m △ O A E num_{△OAB}+num_{△OBC}+num_{△OCD}-num_{△OED}-num_{△OAE} numOAB+numOBC+numOCDnumOEDnumOAE。不难得出结论:

多边形的num=Σ多边形中的 某些边 与点O所形成的三角形的num-Σ多边形中的 其它边 与点O所形成的三角形的num

在这里插入图片描述
我们不妨把这些点极角排序,接着顺时针(或逆时针)扫这些边。假如现在扫到边 A i A i + 1 A_iA_{i+1} AiAi+1,如果 A i A_i Ai的极角序大于 A i + 1 A_{i+1} Ai+1的极角序,那么就加上这条边与O形成三角形的num,否则减去这条边的num。这样子结果的绝对值等于答案。

现在问题就是怎么快速求出图中任意一个三角形的num。

考虑割补法,发现对于一个三角形ABO(如下图), n u m △ A B O = n u m △ A O A ′ − n u m △ B O A ′ num_{△ABO}=num_{△AOA'}-num_{△BOA'} numABO=numAOAnumBOA(A’是AB的延长线与x轴的交点)。
在这里插入图片描述

我们以每个顶点为原点,给所有的青蛙点、顶点以及原点极角排序(这里可以以第三象限最小、第二象限最大的极角值来排序),然后维护一个前缀和数组 s u m i , j sum_{i,j} sumi,j记录以点 i 为原点,对其他点进行极角排序,其中 j 以及极角值比 j 的要小的点的价值和(这里不妨把顶点和原点视作价值为0的青蛙)。

由于题目保证了无三点共线的情况,而有两点与原点共线的情况又很好处理(只用让原点的极角序比与它极角值相等的点的极角序小就行了),这样就能快速求出 n u m △ A O A ′ num_{△AOA'} numAOA了(其实就是 s u m A , B − s u m A , O sum_{A,B}-sum_{A,O} sumA,BsumA,O 。但是 n u m △ B O A ′ num_{△BOA'} numBOA怎么求呢?可以在以顶点 A i A_i Ai为原点时,对于每一个顶点 A j ( i ≠ j ) A_j(i\not=j) Aj(i=j),都新建一个 A j A_j Aj关于 A i A_i Ai对称的点 A j ′ A_j' Aj,然后和上面一样处理就行了。

时间复杂度 O ( n log ⁡ ( n + m ) + ∑ s ) O(n\log(n+m)+\sum s) O(nlog(n+m)+s)

x i a o q i xiaoqi xiaoqi大佬在求一个三角形的num时做法不同,他是把以每一个顶点为原点给其它点排好序,算出前缀和。然后询问时用余弦定理算出∠A’OB,二分求价值和。

CODE

#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
#define llf long double
#define M 10001
#define N 1005
struct node{
    
    int x,y,id,type;long double ang;}a[N],b[N],c[3005];
int n,v[N],arr[N],sum[N][N],_sum[N][N],edge[N][N];
inline char gc()
{
    
    
	static char buf[100005],*l=buf,*r=buf;
	return l==r&&(r=(l=buf)+fread(buf,1,100005,stdin),l==r)?EOF:*l++;
}
inline void read(int &k)
{
    
    
	char ch;bool sign=0;
	while(ch=gc(),ch!='-'&&(ch<'0'||ch>'9'));
	if(ch=='-') sign=1,ch=gc();k=ch-'0';
	while(ch=gc(),ch>='0'&&ch<='9') k=k*10+ch-'0';
	if(sign) k=-k;
}
inline int abs(int x,int y){
    
    return x<0?-x:x;}
inline bool cmp(node x,node y)
{
    
    return x.ang!=y.ang?x.ang<y.ang:abs(x.x)>abs(y.x);}
inline int calc(int x,int y)
{
    
    
	int sign=cmp(a[x],a[y])?1:-1;
	if(sign>0) x^=y,y^=x,x^=y;
	return sign*(sum[x][y]+sum[y][n+1]-sum[x][n+1]-_sum[y][x]);
}
int main()
{
    
    
	int m,q,k,s,cnt;
	read(n),read(m);
	for(int i=1;i<=n;++i) read(a[i].x),read(a[i].y),a[i].ang=atan2((llf)a[i].y,(llf)a[i].x);
	for(int i=1;i<=m;++i) read(b[i].x),read(b[i].y),read(v[i]),b[i].ang=atan2((llf)b[i].y,(llf)b[i].x);
	for(int i=1;i<=n;++i) a[i].x+=M,a[i].y+=M;
	for(int i=1;i<=m;++i) b[i].x+=M,b[i].y+=M;
	for(int i=1;i<=n;++i)
	{
    
    
		s=1,cnt=0,c[1]=(node){
    
    -a[i].x,-a[i].y,n+1,0};
		for(int j=1;j<=n;++j) if(i^j)
			c[++s]=(node){
    
    a[j].x-a[i].x,a[j].y-a[i].y,j,0,atan2((llf)(a[j].y-a[i].y),(llf)(a[j].x-a[i].x))},
			c[++s]=(node){
    
    a[i].x-a[j].x,a[i].y-a[j].y,j,1,atan2((llf)(a[i].y-a[j].y),(llf)(a[i].x-a[j].x))};
		for(int j=1;j<=m;++j) c[++s]=(node){
    
    b[j].x-a[i].x,b[j].y-a[i].y,j,2,atan2((llf)(b[j].y-a[i].y),(llf)(b[j].x-a[i].x))};
		sort(c+1,c+s+1,cmp);
		for(int j=1;j<=s;++j)
		{
    
    
			if(!c[j].type) sum[i][c[j].id]=cnt;
			else if(c[j].type==1) _sum[i][c[j].id]=cnt;
			else cnt+=v[c[j].id];
		}
	}
	for(int i=1;i<=n;++i)
		for(int j=1;j<i;++j)
			edge[i][j]=calc(i,j),edge[j][i]=-edge[i][j];
	read(q);
	for(int p=1;p<=q;++p)
	{
    
    
		read(k);
		for(int i=1;i<=k;++i) read(arr[i]);
		cnt=edge[arr[k]][arr[1]];
		for(int i=1;i<k;++i) cnt+=edge[arr[i]][arr[i+1]];
		printf("%d\n",abs(cnt));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huangzihaoal/article/details/108958885
今日推荐