bzoj2787 Spoj COT4 (11482). Count on a trie

先给个这道题的链接:spoj bzoj

最近我重新复(学)习了一遍后缀自动机,发现原来有好多的东西已经全都忘掉了,这道题是我做kuangbin专题加强版时遇到的最难的一道题,也是到目前为止基本上是我写过的代码里最长的一个(树套树都没这么长啊QAQ),写了我整整一天

我做的专题是:打个广告,好像打不开,网址是 https://vjudge.net/contest/214869#overview

代码长度:压行以后,把debug的全删掉250行,加上就320行了,如果format一下就更长了,快400行了吧

时间:bzoj现在排名第4,2100ms,vjudge(spoj)上570ms,现在是第2,还是比较快的(orz rank1的大佬,好像是基本一样的方法,但是看都看不懂)

update:加了个输入挂,然后spoj->260ms,加了个输出挂,然后spoj->250ms,bzoj->1412ms,全都rank1了好爽啊,想要输入输出挂代码的可以去vjudge上找下,rank1感觉可能是由于机器升级了,这道题又没啥人做造成的

做法:主要参考了两个blog:blog1 blog2 还有这个blog3

虽然我由于我太菜,基本思路全都是参考的第二个blog,感觉写的都和第二个blog里的都差不多了,这里可以认为基本上是对那个blog的补充

题意我就不详细说了,主要是说:

两组串的set,S里是格式串,T串是目标串,求T中某串Ti在S中Si出现了几次

S串的增加方法是从一个已有的串往后加个字符,生成新串

T串的增加方法是从一个已有的串向前或者向后加个字符;或者两个已有的串连到一块生成新串

详细讲讲做法:(在线的我实在是不会啊,感觉怎么也不能直接做,或许后缀平衡树用替罪羊树均摊可做?感觉不太对的样子)

首先,我们知道一个定理,后缀自动机的fail树是反串的后缀树,然后我们利用这个定理来做这道题:

在线做的话,S串和T串都是会变化的,虽然S串变化不大,但是离线显然会简化问题:

然后我们就离线做了:

由于:第一,一个字符很简单就能找到在任意串出现的次数,第二,T串的增加方式和S串是一样的,所以我们只需要解决一个问题:两个串Ta和Tb,而且你知道Ta串在任意串中出现的次数和Tb串在任意串出现的次数,怎样维护加起来的串在任意串中才出现的次数呢?(这里维护任意串是因为如果只维护一个串或者某些串,我不知道怎样能让复杂度是对的)

这里我自己觉得只有hash一下才可以做,然后就GG了,但是其实有个如果从后缀树来反着看的话,有一些性质:

首先,如果将trie的串从小到大加到后缀自动机上的话,有个性质就是加入的点fail上长度是不一样的,也就是说从fail网往上看,每个点代表的串都不同,而且fail树上从0往下看每个开始的字符一定不同,否则就矛盾了

然后如果我们在trie上bfs,同时加进后缀自动机,就能够把trie按后缀排序了(这可能是个套路,然后我太菜并不知道)

那么我们对于T串的每一个串维护一个[L,R],表示作为一个串能在之前的后缀上的rank,然后合并时二分一下,然后在trie上求kth-father,就能够得出连起来的[L',R'] 了(二分然后将前串的rank和kth-father的比一比,这里的二分写的我好难受啊)

然后,由于这里求father比较多,或者说特别多,如果这里倍增来求kth-father,常数较大,而且多个log,坑爹的卡常(或者本意就是要卡这个log),导致过不了这道题。如果用32位的预处理是可过的,但是有更好的方法:kth-father有个利用长链剖分,O(nlogn)预处理,O(1)查询的ladder+倍增的方法,用这个方法就能过了(我写的时候还写挂了好长时间。。注意ladder只有top相等时要push),链接:zqc的blog

下面给下我的代码(为了清楚,不带输入输出挂)

代码
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define REP(I,N) for (I=0;I=0;I--)
#define rep(I,S,N) for (I=S;I=S;I--)
#define FOR(I,S,N) for (I=S;I<=N;I++)
#define rFOR(I,S,N) for (I=N;I>=S;I--)
typedef unsigned long long ULL;
typedef long long LL;
const int INF=0x3f3f3f3f;
const LL INFF=0x3f3f3f3f3f3f3f3fll;
const LL M=1e9+7;
const LL maxn=3e5+7;
const double eps=0.00000001;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
templateinline T abs(T a) {return a>0?a:-a;}
templateinline T powMM(T a,T b){T ret=1;for (;b;b>>=1ll,a=1ll*a*a%M) if (b&1) ret=1ll*ret*a%M;return ret;}//SPOJ COT4
struct SAM{const static int maxn=2e5+7;int next[maxn][26],fail[maxn],len[maxn],cnt,pos[maxn],Pos[maxn];void init(){cnt=0;fail[0]=-1;len[0]=0;pos[0]=0;memset(next[0],0,sizeof(next[0]));}int add(int p,int c,int id){int np=++cnt;pos[np]=Pos[np]=id;memset(next[np],0,sizeof(next[np]));len[np]=len[p]+1;for (;p!=-1&&!next[p][c];p=fail[p]) next[p][c]=np;if (p==-1) fail[np]=0;else {int q=next[p][c];if (len[p]+1==len[q]) fail[np]=q;else{int nq=++cnt;len[nq]=len[p]+1;memcpy(next[nq],next[q],sizeof(next[q]));fail[nq]=fail[q];pos[nq]=pos[q];Pos[nq]=0;fail[np]=fail[q]=nq;for (;p!=-1&&next[p][c]==q;p=fail[p]) next[p][c]=nq;}}return np;}int failnext[maxn][26];
};
struct TRIE{SAM sam;const static int maxn=1e5+26+7;void init(){sam.init();tot=0;ToT=1;id[ToT]=0;val[0]=-1;//1:空memset(next[0],0,sizeof(next[0]));}//1:trie上建树int id[maxn],ToT;//queriesint next[maxn][26],last[maxn],tot;//on the trieint val[maxn];void Add(int i,int c){int p=id[i];ToT++;if (!next[p][c]) {next[p][c]=++tot;val[tot]=c;memset(next[tot],0,sizeof(next[tot]));fa[tot][0]=p;}id[ToT]=next[p][c];}int Q[maxn],st,ed;void buildSAM(){st=ed=0;Q[ed++]=0;while (st!=ed){int p=Q[st++];char c;REP(c,26) if (next[p][c]){int nxt=next[p][c];last[nxt]=sam.add(last[p],c,nxt);Q[ed++]=nxt;}}}//2:get L-Rint failtot;int rank[maxn],sa[maxn];void dfsrank(int x){if (sam.Pos[x]) rank[sam.Pos[x]]=++failtot,sa[failtot]=sam.Pos[x];char c;REP(c,26) if (sam.failnext[x][c]) dfsrank(sam.failnext[x][c]);}void linkfail(){int i;memset(sam.failnext,0,sizeof(sam.failnext[0])*(sam.cnt+1));FOR(i,1,sam.cnt)sam.failnext[sam.fail[i]][val[prev(sam.pos[i],sam.len[sam.fail[i]])]]=i;dfsrank(0);}//3:build_fa; ladder长链剖分int fa[maxn][21],son[maxn],top[maxn],len[maxn],dep[maxn];vector ladder[maxn],upper[maxn];int upp[maxn];void buildfa(){int i,j,c;dep[0]=0;FOR(i,1,tot) rep(j,1,21)fa[i][j]=fa[fa[i][j-1]][j-1],dep[i]=dep[fa[i][0]]+1;rFOR(i,0,tot){int o=0;top[i]=i;ladder[i].clear();REP(c,26) if (next[i][c]){int p=next[i][c];if (!o||len[o]B.r||A.l>A.r) return node(0,-1,A.len+B.len);int l,r,L,R;l=B.r+1;r=B.l-1;for(L=B.l,R=B.r;L<=R;){int mid=(L+R)/2;if (trie.rank[trie.prev(trie.sa[mid],B.len)]A.r) R=mid-1;else L=mid+1,r=mid;}return node(l,r,A.len+B.len);
}
//4:solve
int F[maxn];
inline int lowbit(int x){return x&-x;}
void update(int x,int val){for (;x<=trie.failtot;x+=lowbit(x)) F[x]+=val;
}int query(int x){int ret=0;for (;x;x-=lowbit(x)) ret+=F[x];return ret;
}int query(int l,int r){if (l>r) return 0;return query(r)-query(l-1);
}
node B[maxn];
int Ans[maxn],head[maxn];
void addnode(int x,int pos,int i){x=trie.id[x];B[i]=A[pos];B[i].next=head[x];head[x]=i;
}
void getans(int x){int i;if (x) update(trie.rank[x],1);for (i=head[x];~i;i=B[i].next){if (B[i].len&&B[i].l<=B[i].r) Ans[i]=query(B[i].l,B[i].r);else Ans[i]=0;}REP(i,26) if (trie.next[x][i]) getans(trie.next[x][i]);if (x) update(trie.rank[x],-1);
}
int n,m,Q;
int i,j,k;
char c;
int main(){scanf("%d",&Q);trie.init();FOR(i,1,Q){scanf("%d",&q[i].op);if (q[i].op==1){scanf("%d %c",&q[i].i,&c);q[i].c=c-'a';trie.Add(q[i].i,q[i].c);}else if (q[i].op==2){scanf("%d%d %c",&q[i].i,&q[i].j,&c);q[i].c=c-'a';}else scanf("%d%d",&q[i].i,&q[i].j);}trie.buildfa();trie.buildSAM();trie.linkfail();REP(i,26){int l,r,L,R;l=trie.failtot+1;r=0;for(L=1,R=trie.failtot;L<=R;){int mid=(L+R)/2;if (trie.val[trie.sa[mid]]i) R=mid-1;else L=mid+1,r=mid;}C[i]=node(l,r,1);}FOR(i,0,trie.tot) head[i]=-1;QAQ=1;FOR(i,1,Q){if (q[i].op==1){head[i]=-1;}else if (q[i].op==2){if (q[i].i==0) A[++QAQ]=merge(C[q[i].c],A[q[i].j]);if (q[i].i==1) A[++QAQ]=merge(A[q[i].j],C[q[i].c]);}else if (q[i].op==3)A[++QAQ]=merge(A[q[i].i],A[q[i].j]);else addnode(q[i].j,q[i].i,i);}getans(0);FOR(i,1,Q) if (q[i].op==4) printf("%d\n",Ans[i]);return 0;
}
/*
*/


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部