原题
大佬源码
整个就一ac自动机,不过把建树文本换成了许多的Q点列,把搜索文本换成了P点列,把平常所熟悉的字符串换成了一个一个点,可以用map函数存。易知,任何一个节点,就是它整个边与上一条边的长度的比值,还要此3个点所拼成的角度,这两个关键量可以用结构体c存。
为了确保精度,边长比例可以用两边平方的比例。而角度就比较麻烦了,当时就卡在这里(晕),后来在网上查到了一种很方便的方法,就是两向量边的叉积和点积的比例可以表示角度。
还有,关于翻转,可以在原来的线上,将y反转后,重新造一条它的翻转线出来。最后还要记录一下,q如果是直线的情况,因为我们是把p翻转,那么就会求2次直线Q的情况,要除以2。
最后,一定要仔细,一定要仔细, 一定要仔细。
#include<bits/stdc++.h>
using namespace std;
const int MX=2e5+9;
int n,m,k,tot=0,fail[MX],last[MX],cnt[MX],ans[MX],tp[MX];
struct point{
int x,y;
point operator - ( const point &rhs){ return point{x-rhs.x,y-rhs.y};}
int operator * (const point &rhs){ return x*rhs.y-y*rhs.x;}
int operator ^ (const point &rhs){ return x*rhs.x+y*rhs.y;}
}po[MX];
struct node{
int a,b,c,d;
bool operator < (const node &rhs) const {
if( a!=rhs.a ) return a<rhs.a;
if( b!=rhs.b ) return b<rhs.b;
if( c!=rhs.c ) return c<rhs.c;
return d<rhs.d;
}
}s[MX];
node change(point a,point b,point c){ // 存两边长的比值还有角度,一定要记得gcd!
node an;
an.a=(b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y);
an.b=(c.x-b.x)*(c.x-b.x)+(c.y-b.y)*(c.y-b.y);
an.c=(b-a)*(c-b),an.d=(b-a)^(c-b);
int t=__gcd(an.a,an.b);
if( t!=0 ) an.a/=t,an.b/=t;
t=__gcd(abs(an.c),abs(an.d));
if( t!=0 ) an.c/=t,an.d/=t;
return an;
}
map<node,int> nex[MX];
vector<int> ed[MX];
#define iter map<node,int>::iterator
void ins(int len,int id){
int now=0;
for( int i=1 ; i<=len ; i++ ){
iter it=nex[now].find(s[i]);
if( it==nex[now].end() ) //看看s[i]是否存在,不在的话就加在now的子节点中
now=nex[now][s[i]]=++tot;
else
now=it->second;
}
ed[now].push_back(id);
}
void build(){
queue<int> que;
for( iter it=nex[0].begin() ; it!=nex[0].end() ; it++ )
que.push(it->second);
while( !que.empty() ){
int now=que.front();
que.pop();
for( iter it=nex[now].begin() ; it!=nex[now].end() ; it++ ){
node a=it->first;
int b=it->second,c=fail[now];
while( c && nex[c].find(a)==nex[c].end() ) c=fail[c]; // 这里找的是个拥有子节点a的now,否则一直找到原节点0,
if( nex[c].find(a)!=nex[c].end() )
c=nex[c][a]; // 因为这是点,不是字符串,所以要这样赋值
fail[b]=c;
last[b]=ed[c].empty()?last[c]:c; // last寻找上一个有ed的节点
que.push(b);
}
}
}
void solve(){
int now=0;
for( int i=1 ; i<=n-2 ; i++ ){
int x=now;
while( x && nex[x].find(s[i])==nex[x].end() ) // 寻找下一个有s[i]的now节点
x=fail[x];
if( nex[x].find(s[i])!=nex[x].end() )
x=nex[x][s[i]];
now=x;
for( ; x ; x=last[x] )
cnt[x]++;
}
}
int main()
{
freopen("input.txt","r",stdin);
scanf("%d %d",&n,&m);
for( int i=1 ; i<=m ; i++ ){
int falg=1;
scanf("%d",&k);
for( int j=1 ; j<=k ; j++ )
scanf("%d %d",&po[j].x,&po[j].y);
if( k<=2 ){
ans[i]=n-1; // 如果就2个点连成的一条线,答案一定是n-1,
continue;
}
for( int j=2 ; j<k ; j++ ){
s[j-1]=change(po[j-1],po[j],po[j+1]);
if( s[j-1].c ) // 判断是否是直线
falg=0;
}
if( falg )
tp[i]=1; // 这个tp是存模板q是否hi是一条直线,如果是一条直线,那么翻转或不翻转就没有意义了
ins(k-2,i); //,但是我们将把p链翻转再求一次,所以如果q是直线,那么最终答案要/2
}
build();
for( int i=1 ; i<=n ; i++ )
scanf("%d %d",&po[i].x,&po[i].y);
for( int i=2 ; i<n ; i++ )
s[i-1]=change(po[i-1],po[i],po[i+1]);
solve();
for( int i=1 ; i<=n ; i++ )
po[i].x=-po[i].x; // 将所需要找的链翻转
for( int i=2 ; i<n ; i++ )
s[i-1]=change(po[i-1],po[i],po[i+1]);
solve();
for( int i=1 ; i<=tot ; i++ )
for( int j=0 ; j<ed[i].size() ; j++ )
ans[ed[i][j]]+=cnt[i];
for( int i=1 ; i<=m ; i++ )
printf("%d\n",ans[i]/(tp[i]+1));
return 0;
}
这种情况在Q例很多的时候,会超时。
#include<bits/stdc++.h>
using namespace std;
const int MX=2e5+9;
int n,m,k,tot=0,fail[MX],last[MX],cnt[MX],ans[MX],tp[MX];
struct point{
int x,y;
point operator - ( const point &rhs){ return point{x-rhs.x,y-rhs.y};}
int operator * (const point &rhs){ return x*rhs.y-y*rhs.x;}
int operator ^ (const point &rhs){ return x*rhs.x+y*rhs.y;}
}po[MX];
struct node{
int a,b,c,d;
bool operator < (const node &rhs) const {
if( a!=rhs.a ) return a<rhs.a;
if( b!=rhs.b ) return b<rhs.b;
if( c!=rhs.c ) return c<rhs.c;
return d<rhs.d;
}
}s[MX];
node change(point a,point b,point c){
node an;
an.a=(b.x-a.x)*(b.x-a.x)+(b.y-a.y)*(b.y-a.y);
an.b=(c.x-b.x)*(c.x-b.x)+(c.y-b.y)*(c.y-b.y);
an.c=(b-a)*(c-b),an.d=(b-a)^(c-b);
int t=__gcd(an.a,an.b);
if( t!=0 ) an.a/=t,an.b/=t;
t=__gcd(abs(an.c),abs(an.d));
if( t!=0 ) an.c/=t,an.d/=t;
return an;
}
map<node,int> nex[MX];
vector<int> ed[MX];
#define iter map<node,int>::iterator
void ins(int len,int id){
int now=0;
for( int i=1 ; i<=len ; i++ ){
iter it=nex[now].find(s[i]);
if( it==nex[now].end() )
now=nex[now][s[i]]=++tot;
else
now=it->second;
}
ed[now].push_back(id);
}
void build(){
queue<int> que;
for( iter it=nex[0].begin() ; it!=nex[0].end() ; it++ )
que.push(it->second);
while( !que.empty() ){
int now=que.front();
que.pop();
for( iter it=nex[now].begin() ; it!=nex[now].end() ; it++ ){
node a=it->first;
int b=it->second,c=fail[now];
while( c && nex[c].find(a)==nex[c].end() ) c=fail[c];
if( nex[c].find(a)!=nex[c].end() )
c=nex[c][a];
fail[b]=c;
last[b]=ed[c].empty()?last[c]:c;
que.push(b);
}
}
}
void solve(){
int now=0;
for( int i=1 ; i<=n-2 ; i++ ){
int x=now;
while( x && nex[x].find(s[i])==nex[x].end() )
x=fail[x];
if( nex[x].find(s[i])!=nex[x].end() )
x=nex[x][s[i]];
now=x;
for( ; x ; x=last[x] )
for( int j=0 ; j<ed[x].size() ; j++ )
ans[ed[x][j]]++;
}
}
int main()
{
// freopen("input.txt","r",stdin);
scanf("%d %d",&n,&m);
for( int i=1 ; i<=m ; i++ ){
int falg=1;
scanf("%d",&k);
for( int j=1 ; j<=k ; j++ )
scanf("%d %d",&po[j].x,&po[j].y);
if( k<=2 ){
ans[i]=n-1;
continue;
}
for( int j=2 ; j<k ; j++ ){
s[j-1]=change(po[j-1],po[j],po[j+1]);
if( s[j-1].c )
falg=0;
}
if( falg )
tp[i]=1;
ins(k-2,i);
}
build();
for( int i=1 ; i<=n ; i++ )
scanf("%d %d",&po[i].x,&po[i].y);
for( int i=2 ; i<n ; i++ )
s[i-1]=change(po[i-1],po[i],po[i+1]);
solve();
for( int i=1 ; i<=n ; i++ )
po[i].x=-po[i].x;
for( int i=2 ; i<n ; i++ )
s[i-1]=change(po[i-1],po[i],po[i+1]);
solve();
for( int i=1 ; i<=m ; i++ )
printf("%d\n",ans[i]/(tp[i]+1));
return 0;
}