题解 P3418 【[POI2005]PUN-Points】

oscar

2017-09-11 01:26:34

Solution

【POI补全计划#2 POI2005 PUN】 先说题目大意: 给定两个点集,判断是否相似,相似输出TAK,不相似输出NIE 相似的定义是可以经过四种操作后重合(平移,旋转,翻转,防缩) 多组数据(具体输入方式见题目) ----------------题解分割线------------------- 这题本来是个很水的题,但是卡我精度卡了好久。。可能是我太菜了。。 简单来说就是找到点集的重心(所有点加起来除以点的个数), 然后将所有点标准化至【最长的线段长度为1,重心在原点上】 这样就解决了平移和防缩的问题 接下来对极角排序后差分一下(**第二关键字为长度**) 最后由于圆上的整点个数比较少,只需要暴力判断其中一个点集是否是另一个旋转/翻转后的结果就行了,不需要KMP **注意要特判有可能有点和重心重合** **注意任何可能会被卡精度的地方** 福利:[原题地址](http://oi.edu.pl/old/php/show.php?ac=e180702) 贴代码时间(不要吐槽我不小心面向对象了QWQ) ```cpp #include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const double eps=1e-10,PI=3.1415926535897932384626; const int MAXN=25050; struct point { double x,y; inline point(){} inline point(double xx,double yy){x=xx,y=yy;} inline point(const point &p){x=p.x,y=p.y;} inline point operator=(const point &p){x=p.x,y=p.y;return *this;} inline point operator+(const point &p)const{return point(x+p.x,y+p.y);} inline point operator+=(const point &p){return *this=*this+p;} inline point operator-(const point &p)const{return point(x-p.x,y-p.y);} inline point operator-=(const point &p){return *this=*this-p;} inline double operator*(const point &p)const{return x*p.x+y*p.y;} inline point operator/(const double &n)const{return point(x/n,y/n);} inline point operator/=(const double &n){return *this=*this/n;} inline bool operator==(const point &p)const{return abs(x-p.x)<eps&&abs(y-p.y)<eps;} }set1[MAXN],set2[MAXN]; struct atom { double len,deg; inline bool operator==(const atom &a)const { return abs(len-a.len)<eps&&abs(deg-a.deg)<eps; } inline bool operator!=(const atom &a)const { return !(*this==a); } inline bool operator<(const atom &a)const { return abs(deg-a.deg)<eps?len<a.len:deg<a.deg; } }revarr1[MAXN],arr1[MAXN],arr2[MAXN]; inline bool cmp(const atom &a,const atom &b) { return a.len<b.len; } inline bool cmp2(const atom &a,const atom &b) { return abs(a.deg-b.deg)<eps?a.len<b.len:a.deg>b.deg; } inline double sqr(const double &x){return x*x;} inline bool match(const atom arr1[],const atom arr2[],int len) { static int tt=0; ++tt; for(int delta=0;delta<len;delta++) { bool flag=true; for(int i=1;i<=len;i++) { if(arr1[i]!=arr2[(i+delta-1)%len+1]) { flag=false; break; } } if(flag)return true; } return false; } int main() { int n,t,n2; bool havecenter=false,havecenter2; scanf("%d",&n); point c1(0,0); for(int i=1;i<=n;i++) { scanf("%lf%lf",&set1[i].x,&set1[i].y); c1+=set1[i]; } c1/=n; for(int i=1;i<=n;i++) { if(!havecenter)set1[i]-=c1; else set1[i]=set1[i+1]-c1; if(set1[i]==point(0,0)) { havecenter=true; n--;i--; continue; } arr1[i].len=sqrt(sqr(set1[i].x)+sqr(set1[i].y)); arr1[i].deg=acos(set1[i].x/arr1[i].len); if(set1[i].y<0)arr1[i].deg=2*PI-arr1[i].deg; } sort(arr1+1,arr1+n+1,cmp); double maxlen=arr1[n].len; for(int i=1;i<=n;i++) { arr1[i].len/=maxlen; revarr1[i]=arr1[i]; } sort(arr1+1,arr1+n+1); sort(revarr1+1,revarr1+n+1,cmp2); double maxdeg=arr1[n].deg; for(int i=n;i>=2;i--) { revarr1[n-i+2].deg=(arr1[i].deg-=arr1[i-1].deg); } revarr1[1].deg=arr1[1].deg+=2*PI-maxdeg; scanf("%d",&t); while(t--) { havecenter2=false; scanf("%d",&n2); for(int i=1;i<=n2;i++) { scanf("%lf%lf",&set2[i].x,&set2[i].y); } point c2(0,0); for(int i=1;i<=n2;i++) { c2+=set2[i]; } c2/=n2; for(int i=1;i<=n2;i++) { if(!havecenter2)set2[i]-=c2; else set2[i]=set2[i+1]-c2; if(set2[i]==point(0,0)) { havecenter2=true; n2--;i--; continue; } arr2[i].len=sqrt(sqr(set2[i].x)+sqr(set2[i].y)); arr2[i].deg=acos(set2[i].x/arr2[i].len); if(set2[i].y<0)arr2[i].deg=2*PI-arr2[i].deg; } if(n2!=n) { printf("NIE\n"); continue; } else if(n==0) { printf("TAK\n"); continue; } sort(arr2+1,arr2+n2+1,cmp); double maxlen=arr2[n2].len; for(int i=1;i<=n2;i++) { arr2[i].len/=maxlen; } sort(arr2+1,arr2+n2+1); double maxdeg=arr2[n2].deg; for(int i=n2;i>=2;i--) { arr2[i].deg-=arr2[i-1].deg; } arr2[1].deg+=2*PI-maxdeg; bool f1=match(arr1,arr2,n),f2=match(revarr1,arr2,n); if(havecenter==havecenter2&&(f1||f2))printf("TAK\n"); else printf("NIE\n"); } return 0; } ```