标签:first 出现 space gcd mat 次数 div2 题解 namespace
??每一个点都是一个二元组 \((x,y)\),定义一个轨迹为一个点列 \(P=(p_1, p_2,\dots,p_n)\) 。轨迹 \(Q = (q_1, q_2,\dots,q_m)\) 在 \(P\) 中的一次出现,当且仅当 \(Q\) 中每一个点经过有限次的平移、旋转、翻转、放缩之后得到 \(Q’\) 是 \(P\) 的一个子序列。
??现在给定一个主轨迹和一些子轨迹,求出每个子轨迹在主轨迹中出现的次数。
??轨迹的相似可以转变为相邻两点的差向量的相似,而在平移、旋转、放缩之后,只要两个轨迹相邻两个差向量所成的有向角的大小,以及向量模比值都相同,这两个轨迹就是相似的。因此,我们可以以向量为节点,有向角和模比值为边构造 AC 自动机,再进行匹配即可。
&emsp,?对于翻转的情况,我们发现翻转两次是没有意义的。因此,如果一个轨迹和它翻转之后的轨迹不相同,就把它翻转之后也加入 AC 自动机。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
const int maxn=4e5+5;
struct cplx{
int x,y;
cplx(int xx=0,int yy=0){
x=xx; y=yy;
}
};
cplx operator + (cplx a,cplx b){
return cplx(a.x+b.x,a.y+b.y);
}
cplx operator - (cplx a,cplx b){
return cplx(a.x-b.x,a.y-b.y);
}
int dot(cplx a,cplx b){
return a.x*b.x+a.y*b.y;
}
int det(cplx a,cplx b){
return a.x*b.y-a.y*b.x;
}
int gcd(int a,int b){
while (b){
swap(a,b); b%=a;
}
return a<0?-a:a;
}
struct node{
int x2,y2,dot,det;
};
node cal(cplx a,cplx b){
node nd;
int t;
nd.x2=dot(a,a); nd.y2=dot(b,b);
t=gcd(nd.x2,nd.y2); nd.x2/=t; nd.y2/=t;
nd.dot=dot(a,b); nd.det=det(a,b);
t=gcd(nd.dot,nd.det); nd.dot/=t; nd.det/=t;
return nd;
}
bool operator < (node a,node b){
if (a.x2!=b.x2) return a.x2<b.x2;
if (a.y2!=b.y2) return a.y2<b.y2;
if (a.dot!=b.dot) return a.dot<b.dot;
if (a.det!=b.det) return a.det<b.det;
return false;
}
int que[maxn];
struct AcAuto{
map<node,int> ch[maxn];
int fail[maxn],last[maxn];
int root,ndcnt,strcnt;
int pos[maxn];
bool end[maxn];
int ans[maxn];
int nnode(){
++ndcnt;
fail[ndcnt]=0;
last[ndcnt]=0;
end[ndcnt]=false;
ans[ndcnt]=0;
ch[ndcnt].clear();
return ndcnt;
}
AcAuto(){
ndcnt=0;
strcnt=0;
root=nnode();
}
void clear(){
ndcnt=0;
strcnt=0;
root=nnode();
}
void add_str(node a[],int n){
int u=root,v;
for (int i=0;i<n;i++){
if (!ch[u].count(a[i]))
ch[u][a[i]]=nnode();
u=ch[u][a[i]];
}
pos[strcnt++]=u;
end[u]=true;
}
void build_fail(){
int u=root,v,w;
int l,r;
node nd;
map<node,int>::iterator it;
l=r=0;
que[r++]=u;
while (l<r){
u=que[l++];
for (it=ch[u].begin();it!=ch[u].end();it++){
v=it->second; nd=it->first;
que[r++]=v;
w=fail[u];
while (w&&!ch[w].count(nd))
w=fail[w];
fail[v]=w?ch[w][nd]:root;
last[v]=end[fail[v]]?fail[v]:last[fail[v]];
}
}
}
void addans(int u){
while (u){
ans[u]++;
u=last[u];
}
}
void query(node a[],int n){
int u=root,v;
addans(u);
for (int i=0;i<n;i++){
while (u&&!ch[u].count(a[i])) u=fail[u];
u=u?ch[u][a[i]]:root;
addans(u);
}
}
} ac_auto;
cplx arr[maxn];
node nd[maxn];
bool div2[maxn];
int main(){
int n,m;
scanf("%d%d",&n,&m);
ac_auto.clear();
for (int i=0;i<m;i++){
int k;
scanf("%d",&k);
for (int j=0;j<k;j++){
scanf("%d%d",&arr[j].x,&arr[j].y);
if (j) arr[j-1]=arr[j]-arr[j-1];
}
k-=2;
for (int j=0;j<k;j++)
nd[j]=cal(arr[j],arr[j+1]);
ac_auto.add_str(nd,k);
for (int j=0;j<k;j++)
nd[j].det=-nd[j].det;
ac_auto.add_str(nd,k);
div2[i]=true;
for (int j=0;j<k;j++)
if (nd[j].det!=0) div2[i]=false;
}
for (int i=0;i<n;i++){
scanf("%d%d",&arr[i].x,&arr[i].y);
if (i) arr[i-1]=arr[i]-arr[i-1];
}
n-=2;
for (int i=0;i<n;i++)
nd[i]=cal(arr[i],arr[i+1]);
ac_auto.build_fail();
ac_auto.query(nd,n);
for (int i=0;i<m;i++)
printf("%d\n",(ac_auto.ans[ac_auto.pos[i*2]]+ac_auto.ans[ac_auto.pos[i*2+1]])/(div2[i]?2:1));
return 0;
}
标签:first 出现 space gcd mat 次数 div2 题解 namespace
原文地址:https://www.cnblogs.com/Kilo-5723/p/10738182.html