题意:
给出一个a序列,m个b序列,a能通过倍数关系能出现多少b串。例如a{ 2 4 8 } b{ 1 2 } 那么a中的{ 2 4 } 和{ 4 8 } 是可以通过b序列通过倍数变成的。
题解:
我们可以把串进行缩放,然后缩放后的串进行匹配。
对于m==1的情况用kmp 其他情况用ac自动机。搞一个分数类进行处理。自动机的边用map,对奇葩的地方就是这里,用map做边,map<Node,int>next[SIZE] 这样就通过map映射Node类型(分数类)来解决节点的存储关系。弄完这个,就是无脑的自动机匹配。可惜哥弄了一个晚上的代码最终以wa结尾。。。找啊找,各种原因,最终发现可能是cnt初始化为0的缘故,因为我的代码写法,会导致冲突!应为map如果不存对应的边,那么会返回0,同样根编号也是0,这样很明显冲突啊,所以各种wa!!!最后参照了别人的代码解决了这个问题。由于g++类无法存太大的数组,因此没写成类的形式。这题好题啊,出题人脑洞太大了,ORZ!
#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define B(x) (1<<(x))
typedef __int64 ll;
const int oo=0x3f3f3f3f;
const ll OO=1LL<<61;
const int MOD=10007;
const int maxn=110000;
const int maxm=1000005;
const int SIZE=1000005;
int Next[maxm];
//const int type=4;
inline int gcd(int a,int b)
{
return b==0 ? a : gcd(b,a%b);
}
struct Node
{
int up,down;
Node(){}
Node(int a,int b){
up=a;down=b;
int temp;
if(up>down)temp=gcd(up,down);
else temp=gcd(down,up);
up/=temp;
down/=temp;
}
bool operator==(const Node& a)const{
return up==a.up&&down==a.down;
}
bool operator!=(const Node& a)const{
return up!=a.up||down!=a.down;
}
bool operator<(const Node& a)const{
if(up!=a.up)
return up<a.up;
return down<a.down;
}
}a[maxn],b[maxn];
void input(Node buff[],int len){
int x,y;
scanf("%d",&x);
for(int i=1;i<len;i++){
scanf("%d",&y);
buff[i]=Node(y,x);
x=y;
}
}
map<Node,int>next[SIZE];
int fail[SIZE],flag[SIZE];
int cnt,root;
int newNode(){
next[cnt].clear();
flag[cnt++]=0;
return cnt-1;
}
void Init(){
cnt=1;
root=newNode();
}
void Insert(Node buff[],int len){
int now=root;
Node k;
for(int i=1;i<=len;i++){
k=buff[i];
if(next[now][k]==0)
next[now][k]=newNode();
now=next[now][k];
}//for
flag[now]++;
}
void build(){
fail[root]=0;
Node k;
queue<int>Q;
///注:next[now][i] <=> it->second ; i <=>it->first;
for(map<Node,int>::iterator it=next[root].begin();it!=next[root].end();++it){
fail[it->second]=root;
Q.push(it->second);
}//for
while(!Q.empty()){
int now=Q.front();Q.pop();
flag[now]+=flag[fail[now]];
for(map<Node,int>::iterator it=next[now].begin();it!=next[now].end();++it){
int temp=fail[now];
int nxt=next[temp][it->first];
while(temp&&!nxt){
temp=fail[temp];
nxt=next[temp][it->first];
}
if(temp) fail[it->second]=nxt;
else fail[it->second]=root;
Q.push(it->second);
}//for
}//while
}
ll Search(Node buff[],int len){
int now=root;
ll ans=0;
for(int i=1;i<=len;i++){
int nxt=next[now][buff[i]];
while(now&&!nxt){
now=fail[now];
nxt=next[now][buff[i]];
}
if(now) now=nxt;
else now=root;
ans+=flag[now];
}//for
return ans;
}
void get_next(Node T[],int len)
{
int i=0;
Next[i]=-1;
int j=-1;
while(i<len)
{
if(j==-1||T[i]==T[j]){
i++;j++;
Next[i]=j;
}else j=Next[j];
}
}
ll kmp(Node S[],Node T[],int lenS,int lenT){
ll ans=0;
int i=0,j=0;
while(i<lenS){
if(j==-1||S[i]==T[j]){
i++;
j++;
}else j=Next[j];
if(j==lenT) ans++;
}//for
return ans;
}
int main(){
int T,m,n,k;
ll ans;
scanf("%d",&T);
while(T--){
Init();
scanf("%d %d",&n,&m);
input(a,n);
ans=0;
if(m>1){
for(int i=1;i<=m;i++){
scanf("%d",&k);
input(b,k);
if(k>1) Insert(b,k-1);
else ans+=n;
}//for
build();
ans+=Search(a,n-1);
}else{
scanf("%d",&k);
input(b,k);
if(k>1){
get_next(b+1,k-1);
ans=kmp(a+1,b+1,n-1,k-1);
}else ans+=n;
}//else
printf("%I64d\n",ans);
}//while
return 0;
}//main
/**
2
4 1
2 4 8 16
2 1 2
5 3
2 4 2 4 6
3 1 2 1
1 5
2 16 8
*/