(赛前练手#7) BZOJ1100 [POI2007]对称轴osi && Excited Round 13 T2(manacher)

1100: [POI2007]对称轴osi

Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 919 Solved: 373
[Submit][Status][Discuss]
Description

FGD小朋友——一个闻名遐迩的年轻数学家——有一个小MM,yours。FGD小朋友非常喜欢他的MM,所以他很乐
意帮助他的MM做数学作业。但是,就像所有科学的容器一样,FGD的大脑拒绝不停地重复思考同样的问题。不幸的
是,yours是一个十分用功的学生,所以她不停地让FGD帮助她检查她的作业。一个阳光明媚的周末,yours的数学
老师布置了非常多的寻找多边形的对称轴的题,足够她做相当长的一段时间了。在此之前FGD已经决定去海边度过
这个难得的假期,不过他还是觉得应该帮助他的MM对付可爱的数学作业。很快地,他找到了解决方案,最好写一个
程序来帮助yours检查她的数学作业。因为FGD并非一个计算机科学家,所以他找到了他的好朋友你,请你帮助他完
成这个任务。请写一个程序:读入多边形的描述计算出每个多边形的对称轴数将计算的结果输出

Input

输入的第一行包含一个正整数t(1<=t<=10),为多边形的边数。接下来,为t个多边形的描述,每个描述的第一
行为一个正整数n(3<=n<=100000),表示了多边形的点数。然后在后面n行每行两个整数x和y(?100000000<=x, y<=1
00000000),依次表示多边形的顶点坐标。多边形不一定是凸的,但是不自交——任何两条边都只有最多一个公共
点——他们的公共端点。此外,没有两条连续的边平行。

Output

你的程序应该输出正好t行,第k行包含了一个整数nk——表示第k个多边形有多少个对称轴。

Sample Input

2

12

1 -1

2 -1

2 1

1 1

1 2

-1 2

-1 1

-2 1

-2 -1

-1 -1

-1 -2

1 -2

6

-1 1

-2 0

-1 -1

1 -1

2 0

1 1

Sample Output

4

2
HINT
在这里插入图片描述

Source

Manacher 推荐文章:https://segmentfault.com/a/1190000008484167
我们发现,对于这样的多边形来说,如果其对称,那么其两边的角和边都将一一对应相等,所以我们可以把边和角组合起来跑一次manacher,对于每个点来说,如果其最大回文长度 >= n (在这里,由于我们同时统计了边和角,所以是2 * n ,所以应该是最大回文半径 >= n(不要说为什么2n - 1 == 2n,我们是进行了奇偶处理的)) , 则对答案贡献一条对称轴。
AC Code:

#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
#define eps 1e-8
using namespace std;
il int read(){rg int x = 0 , w = 1 ; rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch == '-') w = -1; ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0' ; ch = getchar();}return x * w;}
struct point{
	int x , y;
	point operator + (const point &b) const {return (point){x + b.x , y + b.y};}
	point operator - (const point &b) const {return (point){x - b.x , y - b.y};}
	int operator * (const point &b) const {return (x * b.x + y * b.y);} 
	int operator ^ (const point &b) const {return (x * b.y - y * b.x);}//用叉积来模拟角
}p[maxn];
int s[maxn << 2] , f[maxn];
int dis(point a , point b){return ((a - b) * (a - b));}//这里如果使用sqrt的话会WA。。(损失了部分精度)
//由于我们只需要判断相等,因此可以如上用 
void init(int n){
	p[0] = p[n] , p[n + 1] = p[1];
	for (rg int i = 1 ; i <= n ; ++i){
		s[(i << 1) - 1] = (p[i - 1] - p[i]) ^ (p[i + 1] - p[i]);
		s[i << 1] = dis(p[i] , p[i + 1]);
	}
	rg int delta = n << 1;
	for (rg int i = 1 ; i <= n ; ++i)
		s[i + delta] = s[i];
}
int manacher(int n){
	s[0] = 394234324;//手动模拟边界,防止溢出 
	s[n * 3 + 1] = 923234234;//同上 
	rg int len = n * 3;
	rg int id = 1 , mx = 0 , cnt = 0;
	for (rg int i = 1 ; i <= len ; ++i){
		f[i] = mx > i ? min(f[(id << 1) - i] , mx - i) : 1;
		while(s[i + f[i]] == s[i - f[i]])
			++f[i];
		if (f[i] + i > mx){
			mx = f[i] + i;
			id = i;
		}
		if (f[i] > n) ++cnt;
	}
	return cnt;
}
int main(){
	//freopen("NOIP.in" , "r" , stdin);
	//freopen("NOIP.out" , "w" , stdout);
	rg int t = read();
	while (t--){
		rg int n = read();
		for (rg int i = 1 ; i <= n ; ++i)
			p[i].x = read() , p[i].y = read();				
		init(n);
		printf("%d\n" , manacher(n));
	}
	return 0;
}	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值