bzoj1039 [ZJOI2008]无序运动Movement AC自动机 计算几何

来源:互联网 发布:算法电子书 编辑:程序博客网 时间:2024/05/15 23:47

对于平移,旋转,放缩,由于任意相邻两线段呈夹角不变,长度比例不变。因此只需要判断相邻两线段长度比例和夹角不变。
夹角可以用叉积和点积的比值以及符号确定。
注意必须保留两个符号。
然后可以用AC自动机计算,每个点的答案是这个点fail树子树中的点被长串经过的次数和。

对于翻转操作只需把长的串翻转再做一遍。
注意判断翻转不变的串和长度小于3的串。

#include <bits/stdc++.h>using namespace std;#define N 200010#define M 1600010int n,m,cnt,tot;int len[M],fail[M],en[M],q[M],ans[M],num[M];bool one[M];map<int,int>ch[M];map<int,int>::iterator it; char getc(){    static const int LEN = 4096;    static char buf[LEN],*S=buf,*T=buf;    if(S == T)    {        T = (S=buf)+fread(buf,1,LEN,stdin);        if(S == T)return EOF;    }    return *S++;}int read(){    static char ch;    static int D;    int tp=0;    while(!isdigit(ch=getc()))        if(ch=='-')tp=1;    for(D=ch-'0'; isdigit(ch=getc());)        D=(D<<3)+(D<<1)+(ch-'0');    return tp ? -D:D;}struct poi{    int x,y;    poi(){}    poi(int x,int y):x(x),y(y){}    friend int operator * (const poi &r1,const poi &r2)        {return r1.x*r2.x+r1.y*r2.y;}    friend int operator ^ (const poi &r1,const poi &r2)        {return r1.x*r2.y-r2.x*r1.y;}    friend poi operator - (const poi &r1,const poi &r2)        {return poi(r1.x-r2.x,r1.y-r2.y);}    int len(){return x*x+y*y;}}a[M],b[N];struct node{    int v1,v2,v3,v4;    node(){}    node(int r1,int r2,int r3,int r4)    {        int t=abs(__gcd(r1,r2));        v1=r1/t;v2=r2/t;v3=r3;v4=r4;        if(!r3)v4= r4<0 ? -1:1;        else if(!r4)v3= r3<0 ? -1:1;        else t=abs(__gcd(r3,r4)),v3/=t,v4/=t;    }    friend bool operator < (const node &r1,const node &r2)    {        return r1.v1==r2.v1 ? (r1.v2==r2.v2 ? (r1.v3==r2.v3 ?             r1.v4<r2.v4:r1.v3<r2.v3):r1.v2<r2.v2):r1.v1<r2.v1;    }    friend bool operator != (const node &r1,const node &r2)        {return r1.v1!=r2.v1||r1.v2!=r2.v2||r1.v3!=r2.v3||r1.v4!=r2.v4;}}st[M],s[N],v[M];int get(node x){    int t=lower_bound(st+1,st+1+cnt,x)-st;    if(st[t]!=x)return 0;    return t;}void match(){    int h,r;q[h=r=1]=1;    while(h<=r)    {        int t=q[h++];        for(it=ch[t].begin();it!=ch[t].end();++it)        {            int t1=fail[t];            while(t1&&!ch[t1].count((*it).first))                t1=fail[t1];            fail[(*it).second]=t1 ? ch[t1][(*it).first]:1;            q[++r]=(*it).second;        }    }}void calc(){    memset(num,0,sizeof(num));    for(int now=1,i=2;i<n;i++)    {        int t=get(s[i]);        while(now&&!ch[now].count(t))            now=fail[now];         now=now ? ch[now][t]:1;        num[now]++;    }    for(int i=tot;i>=1;i--)        num[fail[q[i]]]+=num[q[i]];    for(int i=1;i<=m;i++)        if(len[i]>2)            ans[i]+=num[en[i]];}int main(){    //freopen("tt.in","r",stdin);    n=read();m=read();    for(int i=1,K;i<=m;i++)    {        len[i]=read();        for(int j=1;j<=len[i];j++)            a[j].x=read(),a[j].y=read();        if(len[i]<=2)continue;        one[i]=1;        for(int j=2;j<len[i];j++)        {            poi p1=a[j]-a[j-1],p2=a[j+1]-a[j];            v[++cnt]=st[cnt]=node(p1.len(),p2.len(),p1*p2,p1^p2);            if(p1^p2)one[i]=0;        }    }    sort(st+1,st+1+cnt);    for(int i=1;i<=n;i++)        b[i].x=read(),b[i].y=read();    tot=1;fail[1]=0;    for(int i=1,cnt=1;i<=m;i++)        if(len[i]>2)        {            int now=1;            for(int j=0;j<len[i]-2;j++,cnt++)            {                int t=get(v[cnt]);                if(!ch[now].count(t))                    ch[now][t]=++tot;                now=ch[now][t];            }            en[i]=now;        }    match();    for(int i=2;i<n;i++)    {        poi p1=b[i]-b[i-1],p2=b[i+1]-b[i];        s[i]=node(p1.len(),p2.len(),p1*p2,p1^p2);    }    calc();    for(int i=2;i<n;i++)s[i].v4=-s[i].v4;    calc();    for(int i=1;i<=m;i++)    {        if(len[i]<=2)printf("%d\n",n-len[i]+1);        else        {            if(one[i])ans[i]/=2;            printf("%d\n",ans[i]);        }    }    return 0;}
0 0
原创粉丝点击