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;
}

  

posted @ 2015-12-04 14:03  Claris  阅读(615)  评论(0编辑  收藏  举报