BZOJ1039 : [ZJOI2008]无序运动Movement
首先不考虑翻转,对于两个等长的序列,如果任意两条相邻边的边长比以及夹角都相等,那么就匹配。
为了避免实数运算,边长比可以上下平方,然后约分。夹角可以用叉积和点积的最简比值来表示,注意上下符号都要保留。
然后将这些信息离散化,转化成数字串匹配问题,建出用Hash表存边的AC自动机后即可解决。
对于翻转,可以考虑将匹配串翻转,然后再做一次,累加答案,注意特判某些翻转后和原串相等的串。
#include<cstdio> #include<algorithm> const int N=200010,M=1600010,U=4194303; int n,m,i,j,k,x,y,en,flag,len[M],l[M],ans[M];bool one[M]; struct P{ int x,y; P(){} P(int _x,int _y){x=_x,y=_y;} P operator-(const P&b){return P(x-b.x,y-b.y);} int len(){return x*x+y*y;} int operator*(const P&b){return x*b.y-y*b.x;} int operator^(const P&b){return x*b.x+y*b.y;} }a[N],A,B; int b[N],c[N]; inline int sig(int x){return x>0?1:-1;} inline int abs(int x){return x>0?x:-x;} int gcd(int a,int b){return b?gcd(b,a%b):a;} struct Num{ int a,b,c,d; Num(){} Num(int A,int B,int C,int D){ int g=gcd(A,B); a=A/g,b=B/g; if(!C)c=0,d=sig(D); else if(!D)c=sig(C),d=0; else{ g=gcd(abs(C),abs(D)); c=C/g,d=D/g; } } bool operator<=(const Num&x){ if(a!=x.a)return a<x.a; if(b!=x.b)return b<x.b; if(c!=x.c)return c<x.c; return d<=x.d; } bool operator!=(const Num&x){ if(a!=x.a)return 1; if(b!=x.b)return 1; if(c!=x.c)return 1; return d!=x.d; } }pool[M]; int cnt,all[M]; inline int cmp(int a,int b){ Num*A=pool+a,*B=pool+b; if(A->a!=B->a)return A->a<B->a; if(A->b!=B->b)return A->b<B->b; if(A->c!=B->c)return A->c<B->c; return A->d<B->d; } inline int getid(const Num&x){ int l=1,r=cnt,mid,t=0; while(l<=r)if(pool[all[mid=(l+r)>>1]]<=x)l=(t=mid)+1;else r=mid-1; if(!t)return 0; if(pool[all[t]]!=x)return 0; return t; } inline void read(int&a){ char c;bool f=0;a=0; while(!((((c=getchar())>='0')&&(c<='9'))||(c=='-'))); if(c!='-')a=c-'0';else f=1; while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0'; if(f)a=-a; } int tot,f[M],q[M],tag[M]; int g[U+1],G[M],ed; struct E{int x,y,z,nxt;E(){}E(int _x,int _y,int _z,int _nxt){x=_x,y=_y,z=_z,nxt=_nxt;}}e[M]; struct S{int y,z,nxt;S(){}S(int _y,int _z,int _nxt){y=_y,z=_z,nxt=_nxt;}}s[M]; inline int son(int x,int y){ int u=(x<<8|y)&U; for(int p=g[u];p;p=e[p].nxt)if(e[p].x==x&&e[p].y==y)return e[p].z; e[++ed]=E(x,y,++tot,g[u]);g[u]=ed; s[ed]=S(y,tot,G[x]);G[x]=ed; return tot; } inline int ask(int x,int y){ int u=(x<<8|y)&U; for(int p=g[u];p;p=e[p].nxt)if(e[p].x==x&&e[p].y==y)return e[p].z; return 0; } void make(){ int h=0,t=0,i,j,x,y,z;f[0]=-1; while(h<=t)for(i=G[x=q[h++]];i;i=s[i].nxt){ y=s[i].y,q[++t]=z=s[i].z; if(x)for(j=f[x];~j;j=f[j])if(k=ask(j,y)){f[z]=k;break;} } } int main(){ read(n),read(m); for(i=1;i<=m;i++){ read(k);len[i]=k; for(j=1;j<=k;j++)read(a[j].x),read(a[j].y); if(k<=2)continue; l[i]=cnt+1; for(flag=1,j=2;j<k;j++){ A=a[j]-a[j-1],B=a[j+1]-a[j]; pool[++cnt]=Num(A.len(),B.len(),A*B,A^B); if(A*B)flag=0; } if(flag)one[i]=1; } for(i=1;i<=cnt;i++)all[i]=i; if(cnt>1)std::sort(all+1,all+cnt+1,cmp); for(i=1;i<=n;i++)read(a[i].x),read(a[i].y); for(i=2;i<n;i++){ A=a[i]-a[i-1],B=a[i+1]-a[i]; b[i]=getid(Num(A.len(),B.len(),A*B,A^B)); c[i]=getid(Num(A.len(),B.len(),-(A*B),A^B)); } for(i=1;i<=m;i++)if(l[i]){ en=l[i]+len[i]-2; for(x=0,j=l[i];j<en;j++)x=son(x,getid(pool[j])); l[i]=x; } make(); for(x=0,i=2;i<n;i++){ while(x&&!ask(x,b[i]))x=f[x]; tag[x=ask(x,b[i])]++; } for(i=tot;i;i--)tag[f[q[i]]]+=tag[q[i]]; for(i=1;i<=m;i++)if(l[i])ans[i]+=tag[l[i]]; for(i=0;i<=tot;i++)tag[i]=0; for(x=0,i=2;i<n;i++){ while(x&&!ask(x,c[i]))x=f[x]; tag[x=ask(x,c[i])]++; } for(i=tot;i;i--)tag[f[q[i]]]+=tag[q[i]]; for(i=1;i<=m;i++)if(l[i]&&!one[i])ans[i]+=tag[l[i]]; for(i=1;i<=m;i++)if(!l[i])printf("%d\n",n-len[i]+1);else printf("%d\n",ans[i]); return 0; }